@backloghq/opslog 0.1.3 → 0.1.4

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/store.js CHANGED
@@ -257,7 +257,7 @@ export class Store {
257
257
  }
258
258
  async loadArchive(segment) {
259
259
  this.ensureOpen();
260
- const segmentPath = this.archiveSegments.find((s) => s.includes(segment));
260
+ const segmentPath = this.archiveSegments.find((s) => s === `archive/archive-${segment}.json`) || this.archiveSegments.find((s) => s.includes(segment));
261
261
  if (!segmentPath)
262
262
  throw new Error(`Archive segment '${segment}' not found`);
263
263
  return loadArchiveSegment(this.dir, segmentPath);
package/dist/validate.js CHANGED
@@ -3,13 +3,24 @@ export function validateOp(raw) {
3
3
  throw new Error("Invalid operation: not an object");
4
4
  }
5
5
  const obj = raw;
6
- if (typeof obj.ts !== "string")
7
- throw new Error("Invalid operation: missing ts");
8
- if (obj.op !== "set" && obj.op !== "delete") {
9
- throw new Error(`Invalid operation: unknown op "${obj.op}"`);
6
+ if (typeof obj.ts !== "string" || obj.ts.length === 0)
7
+ throw new Error("Invalid operation: ts must be a non-empty string");
8
+ if (typeof obj.op !== "string" || (obj.op !== "set" && obj.op !== "delete")) {
9
+ throw new Error(`Invalid operation: op must be "set" or "delete", got "${obj.op}"`);
10
10
  }
11
- if (typeof obj.id !== "string")
12
- throw new Error("Invalid operation: missing id");
11
+ if (typeof obj.id !== "string" || obj.id.length === 0)
12
+ throw new Error("Invalid operation: id must be a non-empty string");
13
+ if (!("prev" in obj))
14
+ throw new Error("Invalid operation: missing prev");
15
+ if (obj.prev !== null && (typeof obj.prev !== "object" || Array.isArray(obj.prev))) {
16
+ throw new Error("Invalid operation: prev must be an object or null");
17
+ }
18
+ if (obj.op === "set" && (!("data" in obj) || obj.data === null))
19
+ throw new Error("Invalid operation: set op must have non-null data");
20
+ if (obj.op === "delete" && obj.prev === null)
21
+ throw new Error("Invalid operation: delete op must have non-null prev");
22
+ if (obj.op === "delete" && "data" in obj)
23
+ throw new Error("Invalid operation: delete op must not have data field");
13
24
  return raw;
14
25
  }
15
26
  export function validateManifest(raw) {
@@ -17,29 +28,33 @@ export function validateManifest(raw) {
17
28
  throw new Error("Invalid manifest: not an object");
18
29
  }
19
30
  const obj = raw;
20
- if (typeof obj.version !== "number" || !Number.isInteger(obj.version) || obj.version < 1) {
21
- throw new Error("Invalid manifest: version must be a positive integer");
31
+ if (typeof obj.version !== "number" || !Number.isFinite(obj.version) || !Number.isInteger(obj.version) || obj.version < 1) {
32
+ throw new Error("Invalid manifest: version must be a positive finite integer");
22
33
  }
23
- if (typeof obj.currentSnapshot !== "string")
34
+ if (typeof obj.currentSnapshot !== "string" || obj.currentSnapshot.length === 0)
24
35
  throw new Error("Invalid manifest: missing currentSnapshot");
25
- if (typeof obj.activeOps !== "string")
36
+ if (typeof obj.activeOps !== "string" || obj.activeOps.length === 0)
26
37
  throw new Error("Invalid manifest: missing activeOps");
27
38
  if (!Array.isArray(obj.archiveSegments))
28
39
  throw new Error("Invalid manifest: archiveSegments must be an array");
40
+ for (const seg of obj.archiveSegments) {
41
+ if (typeof seg !== "string" || seg.length === 0)
42
+ throw new Error("Invalid manifest: archiveSegments entries must be non-empty strings");
43
+ }
29
44
  if (typeof obj.stats !== "object" || obj.stats === null || Array.isArray(obj.stats)) {
30
45
  throw new Error("Invalid manifest: missing stats");
31
46
  }
32
47
  const stats = obj.stats;
33
- if (typeof stats.activeRecords !== "number")
34
- throw new Error("Invalid manifest: stats.activeRecords must be a number");
35
- if (typeof stats.archivedRecords !== "number")
36
- throw new Error("Invalid manifest: stats.archivedRecords must be a number");
37
- if (typeof stats.opsCount !== "number")
38
- throw new Error("Invalid manifest: stats.opsCount must be a number");
39
- if (typeof stats.created !== "string")
40
- throw new Error("Invalid manifest: stats.created must be a string");
41
- if (typeof stats.lastCheckpoint !== "string")
42
- throw new Error("Invalid manifest: stats.lastCheckpoint must be a string");
48
+ if (typeof stats.activeRecords !== "number" || !Number.isFinite(stats.activeRecords) || !Number.isInteger(stats.activeRecords) || stats.activeRecords < 0)
49
+ throw new Error("Invalid manifest: stats.activeRecords must be a non-negative integer");
50
+ if (typeof stats.archivedRecords !== "number" || !Number.isFinite(stats.archivedRecords) || !Number.isInteger(stats.archivedRecords) || stats.archivedRecords < 0)
51
+ throw new Error("Invalid manifest: stats.archivedRecords must be a non-negative integer");
52
+ if (typeof stats.opsCount !== "number" || !Number.isFinite(stats.opsCount) || !Number.isInteger(stats.opsCount) || stats.opsCount < 0)
53
+ throw new Error("Invalid manifest: stats.opsCount must be a non-negative integer");
54
+ if (typeof stats.created !== "string" || stats.created.length === 0)
55
+ throw new Error("Invalid manifest: stats.created must be a non-empty string");
56
+ if (typeof stats.lastCheckpoint !== "string" || stats.lastCheckpoint.length === 0)
57
+ throw new Error("Invalid manifest: stats.lastCheckpoint must be a non-empty string");
43
58
  return raw;
44
59
  }
45
60
  export function validateSnapshot(raw) {
@@ -47,11 +62,11 @@ export function validateSnapshot(raw) {
47
62
  throw new Error("Invalid snapshot: not an object");
48
63
  }
49
64
  const obj = raw;
50
- if (typeof obj.version !== "number" || !Number.isInteger(obj.version) || obj.version < 1) {
51
- throw new Error("Invalid snapshot: version must be a positive integer");
65
+ if (typeof obj.version !== "number" || !Number.isFinite(obj.version) || !Number.isInteger(obj.version) || obj.version < 1) {
66
+ throw new Error("Invalid snapshot: version must be a positive finite integer");
52
67
  }
53
- if (typeof obj.timestamp !== "string")
54
- throw new Error("Invalid snapshot: missing timestamp");
68
+ if (typeof obj.timestamp !== "string" || obj.timestamp.length === 0)
69
+ throw new Error("Invalid snapshot: timestamp must be a non-empty string");
55
70
  if (typeof obj.records !== "object" || obj.records === null || Array.isArray(obj.records)) {
56
71
  throw new Error("Invalid snapshot: records must be an object");
57
72
  }
@@ -62,13 +77,13 @@ export function validateArchiveSegment(raw) {
62
77
  throw new Error("Invalid archive segment: not an object");
63
78
  }
64
79
  const obj = raw;
65
- if (typeof obj.version !== "number" || !Number.isInteger(obj.version) || obj.version < 1) {
66
- throw new Error("Invalid archive segment: version must be a positive integer");
80
+ if (typeof obj.version !== "number" || !Number.isFinite(obj.version) || !Number.isInteger(obj.version) || obj.version < 1) {
81
+ throw new Error("Invalid archive segment: version must be a positive finite integer");
67
82
  }
68
- if (typeof obj.period !== "string")
69
- throw new Error("Invalid archive segment: missing period");
70
- if (typeof obj.timestamp !== "string")
71
- throw new Error("Invalid archive segment: missing timestamp");
83
+ if (typeof obj.period !== "string" || obj.period.length === 0)
84
+ throw new Error("Invalid archive segment: period must be a non-empty string");
85
+ if (typeof obj.timestamp !== "string" || obj.timestamp.length === 0)
86
+ throw new Error("Invalid archive segment: timestamp must be a non-empty string");
72
87
  if (typeof obj.records !== "object" || obj.records === null || Array.isArray(obj.records)) {
73
88
  throw new Error("Invalid archive segment: records must be an object");
74
89
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backloghq/opslog",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Embedded event-sourced document store. Append-only operation log with immutable snapshots, zero native dependencies.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",