mundane-sdk 0.0.1 → 0.1.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/dist/src/errors.d.ts +9 -9
- package/dist/src/errors.js +20 -20
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +15 -24
- package/dist/src/lock.js +1 -1
- package/dist/src/names.d.ts +1 -1
- package/dist/src/names.js +1 -1
- package/dist/src/schema.js +9 -11
- package/dist/test/basic.test.js +7 -7
- package/package.json +1 -1
package/dist/src/errors.d.ts
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Custom error classes thrown by mundane.
|
|
3
3
|
*
|
|
4
|
-
* -
|
|
5
|
-
* -
|
|
4
|
+
* - LockedError: another live process holds the file lock.
|
|
5
|
+
* - SerializationError: a step's return value doesn't survive a
|
|
6
6
|
* JSON round-trip (Date, undefined, BigInt, Map, Set, function, circular).
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
7
|
+
* - SchemaError: file exists but meta.schema_version != "1".
|
|
8
|
+
* - StepFailedError: a step body threw; wraps the underlying error.
|
|
9
9
|
*/
|
|
10
|
-
export declare class
|
|
10
|
+
export declare class LockedError extends Error {
|
|
11
11
|
readonly code = "EMUNDANELOCKED";
|
|
12
12
|
constructor(message: string);
|
|
13
13
|
}
|
|
14
|
-
export declare class
|
|
14
|
+
export declare class SerializationError extends Error {
|
|
15
15
|
readonly code = "EMUNDANESERIALIZATION";
|
|
16
16
|
readonly path?: string;
|
|
17
17
|
constructor(message: string, path?: string);
|
|
18
18
|
}
|
|
19
|
-
export declare class
|
|
19
|
+
export declare class SchemaError extends Error {
|
|
20
20
|
readonly code = "EMUNDANESCHEMA";
|
|
21
21
|
constructor(message: string);
|
|
22
22
|
}
|
|
23
|
-
export declare class
|
|
23
|
+
export declare class DuplicateStepError extends Error {
|
|
24
24
|
readonly code = "EMUNDANEDUPLICATE";
|
|
25
25
|
readonly stepName: string;
|
|
26
26
|
constructor(stepName: string);
|
|
27
27
|
}
|
|
28
|
-
export declare class
|
|
28
|
+
export declare class StepFailedError extends Error {
|
|
29
29
|
readonly code = "EMUNDANESTEPFAILED";
|
|
30
30
|
readonly stepName: string;
|
|
31
31
|
readonly original: unknown;
|
package/dist/src/errors.js
CHANGED
|
@@ -2,51 +2,51 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Custom error classes thrown by mundane.
|
|
4
4
|
*
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
5
|
+
* - LockedError: another live process holds the file lock.
|
|
6
|
+
* - SerializationError: a step's return value doesn't survive a
|
|
7
7
|
* JSON round-trip (Date, undefined, BigInt, Map, Set, function, circular).
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
8
|
+
* - SchemaError: file exists but meta.schema_version != "1".
|
|
9
|
+
* - StepFailedError: a step body threw; wraps the underlying error.
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
13
|
-
class
|
|
12
|
+
exports.StepFailedError = exports.DuplicateStepError = exports.SchemaError = exports.SerializationError = exports.LockedError = void 0;
|
|
13
|
+
class LockedError extends Error {
|
|
14
14
|
code = "EMUNDANELOCKED";
|
|
15
15
|
constructor(message) {
|
|
16
16
|
super(message);
|
|
17
|
-
this.name = "
|
|
17
|
+
this.name = "LockedError";
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
exports.
|
|
21
|
-
class
|
|
20
|
+
exports.LockedError = LockedError;
|
|
21
|
+
class SerializationError extends Error {
|
|
22
22
|
code = "EMUNDANESERIALIZATION";
|
|
23
23
|
path;
|
|
24
24
|
constructor(message, path) {
|
|
25
25
|
super(message);
|
|
26
|
-
this.name = "
|
|
26
|
+
this.name = "SerializationError";
|
|
27
27
|
this.path = path;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
exports.
|
|
31
|
-
class
|
|
30
|
+
exports.SerializationError = SerializationError;
|
|
31
|
+
class SchemaError extends Error {
|
|
32
32
|
code = "EMUNDANESCHEMA";
|
|
33
33
|
constructor(message) {
|
|
34
34
|
super(message);
|
|
35
|
-
this.name = "
|
|
35
|
+
this.name = "SchemaError";
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
exports.
|
|
39
|
-
class
|
|
38
|
+
exports.SchemaError = SchemaError;
|
|
39
|
+
class DuplicateStepError extends Error {
|
|
40
40
|
code = "EMUNDANEDUPLICATE";
|
|
41
41
|
stepName;
|
|
42
42
|
constructor(stepName) {
|
|
43
43
|
super(`duplicate step name: ${stepName}`);
|
|
44
|
-
this.name = "
|
|
44
|
+
this.name = "DuplicateStepError";
|
|
45
45
|
this.stepName = stepName;
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
exports.
|
|
49
|
-
class
|
|
48
|
+
exports.DuplicateStepError = DuplicateStepError;
|
|
49
|
+
class StepFailedError extends Error {
|
|
50
50
|
code = "EMUNDANESTEPFAILED";
|
|
51
51
|
stepName;
|
|
52
52
|
original;
|
|
@@ -55,7 +55,7 @@ class MundaneStepFailedError extends Error {
|
|
|
55
55
|
? `step ${JSON.stringify(stepName)} failed: ${original.message}`
|
|
56
56
|
: `step ${JSON.stringify(stepName)} failed: ${String(original)}`;
|
|
57
57
|
super(msg);
|
|
58
|
-
this.name = "
|
|
58
|
+
this.name = "StepFailedError";
|
|
59
59
|
this.stepName = stepName;
|
|
60
60
|
this.original = original;
|
|
61
61
|
if (original instanceof Error && original.stack) {
|
|
@@ -63,4 +63,4 @@ class MundaneStepFailedError extends Error {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
-
exports.
|
|
66
|
+
exports.StepFailedError = StepFailedError;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* See ../../../SPEC.md (project root) for the full contract.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
12
|
-
export {
|
|
11
|
+
import { DuplicateStepError, LockedError, SchemaError, SerializationError, StepFailedError } from "./errors";
|
|
12
|
+
export { DuplicateStepError, LockedError, SchemaError, SerializationError, StepFailedError };
|
|
13
13
|
export type Json = null | boolean | number | string | Json[] | {
|
|
14
14
|
[k: string]: Json;
|
|
15
15
|
};
|
package/dist/src/index.js
CHANGED
|
@@ -10,16 +10,16 @@
|
|
|
10
10
|
* See ../../../SPEC.md (project root) for the full contract.
|
|
11
11
|
*/
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.
|
|
13
|
+
exports.StepFailedError = exports.SerializationError = exports.SchemaError = exports.LockedError = exports.DuplicateStepError = void 0;
|
|
14
14
|
exports.run = run;
|
|
15
15
|
const db_1 = require("./db");
|
|
16
16
|
const duration_1 = require("./duration");
|
|
17
17
|
const errors_1 = require("./errors");
|
|
18
|
-
Object.defineProperty(exports, "
|
|
19
|
-
Object.defineProperty(exports, "
|
|
20
|
-
Object.defineProperty(exports, "
|
|
21
|
-
Object.defineProperty(exports, "
|
|
22
|
-
Object.defineProperty(exports, "
|
|
18
|
+
Object.defineProperty(exports, "DuplicateStepError", { enumerable: true, get: function () { return errors_1.DuplicateStepError; } });
|
|
19
|
+
Object.defineProperty(exports, "LockedError", { enumerable: true, get: function () { return errors_1.LockedError; } });
|
|
20
|
+
Object.defineProperty(exports, "SchemaError", { enumerable: true, get: function () { return errors_1.SchemaError; } });
|
|
21
|
+
Object.defineProperty(exports, "SerializationError", { enumerable: true, get: function () { return errors_1.SerializationError; } });
|
|
22
|
+
Object.defineProperty(exports, "StepFailedError", { enumerable: true, get: function () { return errors_1.StepFailedError; } });
|
|
23
23
|
const lock_1 = require("./lock");
|
|
24
24
|
const names_1 = require("./names");
|
|
25
25
|
const schema_1 = require("./schema");
|
|
@@ -29,16 +29,16 @@ function checkJsonRoundtrip(value) {
|
|
|
29
29
|
text = JSON.stringify(value);
|
|
30
30
|
}
|
|
31
31
|
catch (e) {
|
|
32
|
-
throw new errors_1.
|
|
32
|
+
throw new errors_1.SerializationError(`value is not JSON-serializable: ${e.message}`);
|
|
33
33
|
}
|
|
34
34
|
if (text === undefined) {
|
|
35
35
|
// JSON.stringify returns undefined for top-level undefined/function/symbol
|
|
36
|
-
throw new errors_1.
|
|
36
|
+
throw new errors_1.SerializationError("value is not JSON-serializable (undefined / function / symbol at top level)");
|
|
37
37
|
}
|
|
38
38
|
const decoded = JSON.parse(text);
|
|
39
39
|
const mismatch = deepDiff(value, decoded, "");
|
|
40
40
|
if (mismatch !== null) {
|
|
41
|
-
throw new errors_1.
|
|
41
|
+
throw new errors_1.SerializationError(`value does not round-trip through JSON at ${JSON.stringify(mismatch)}`, mismatch);
|
|
42
42
|
}
|
|
43
43
|
return text;
|
|
44
44
|
}
|
|
@@ -109,12 +109,12 @@ class TaskState {
|
|
|
109
109
|
}
|
|
110
110
|
checkSeen(name) {
|
|
111
111
|
if (this.seen.has(name)) {
|
|
112
|
-
throw new errors_1.
|
|
112
|
+
throw new errors_1.DuplicateStepError(name);
|
|
113
113
|
}
|
|
114
114
|
this.seen.add(name);
|
|
115
115
|
}
|
|
116
116
|
async loadCache() {
|
|
117
|
-
const rows = await this.db.all("SELECT
|
|
117
|
+
const rows = await this.db.all("SELECT name, kind, encoding, result, status, error FROM mundane_steps ORDER BY id");
|
|
118
118
|
for (const row of rows)
|
|
119
119
|
this.cache.set(row.name, row);
|
|
120
120
|
}
|
|
@@ -132,10 +132,9 @@ class TaskState {
|
|
|
132
132
|
existing.error = null;
|
|
133
133
|
return existing;
|
|
134
134
|
}
|
|
135
|
-
const now = new Date().toISOString();
|
|
136
135
|
await this.db.run("INSERT INTO mundane_steps (name, kind, encoding, result, status, started_at) " +
|
|
137
|
-
"VALUES (?, ?, ?, NULL, 'pending', ?)", [name, kind, encoding,
|
|
138
|
-
const row =
|
|
136
|
+
"VALUES (?, ?, ?, NULL, 'pending', ?)", [name, kind, encoding, new Date().toISOString()]);
|
|
137
|
+
const row = { name, kind, encoding, result: null, status: "pending", error: null };
|
|
139
138
|
this.cache.set(name, row);
|
|
140
139
|
return row;
|
|
141
140
|
}
|
|
@@ -176,7 +175,7 @@ class ContextImpl {
|
|
|
176
175
|
catch (e) {
|
|
177
176
|
const msg = e instanceof Error ? e.message : String(e);
|
|
178
177
|
await this.task.commitFailed(name, msg);
|
|
179
|
-
throw new errors_1.
|
|
178
|
+
throw new errors_1.StepFailedError(name, e);
|
|
180
179
|
}
|
|
181
180
|
const text = checkJsonRoundtrip(value);
|
|
182
181
|
await this.task.commitDone(name, "json", text);
|
|
@@ -205,15 +204,7 @@ class ContextImpl {
|
|
|
205
204
|
}
|
|
206
205
|
}
|
|
207
206
|
async function run(path, fn) {
|
|
208
|
-
|
|
209
|
-
try {
|
|
210
|
-
lock = await (0, lock_1.acquireLock)(path);
|
|
211
|
-
}
|
|
212
|
-
catch (e) {
|
|
213
|
-
if (e instanceof errors_1.MundaneLockedError)
|
|
214
|
-
throw e;
|
|
215
|
-
throw e;
|
|
216
|
-
}
|
|
207
|
+
const lock = await (0, lock_1.acquireLock)(path);
|
|
217
208
|
let db = null;
|
|
218
209
|
try {
|
|
219
210
|
db = await (0, db_1.openDb)(path);
|
package/dist/src/lock.js
CHANGED
|
@@ -53,7 +53,7 @@ async function acquireLock(path) {
|
|
|
53
53
|
await closeFd(fd);
|
|
54
54
|
const code = e.code;
|
|
55
55
|
if (code === "EWOULDBLOCK" || code === "EAGAIN") {
|
|
56
|
-
throw new errors_1.
|
|
56
|
+
throw new errors_1.LockedError(`${path}: locked by another process`);
|
|
57
57
|
}
|
|
58
58
|
throw e;
|
|
59
59
|
}
|
package/dist/src/names.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Step-name validation. Names must match /^[A-Za-z0-9][A-Za-z0-9._-]*$/.
|
|
3
|
-
* Duplicate names within one task body raise
|
|
3
|
+
* Duplicate names within one task body raise DuplicateStepError;
|
|
4
4
|
* see ../src/index.ts.
|
|
5
5
|
*/
|
|
6
6
|
export declare function validateName(name: string): void;
|
package/dist/src/names.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* Step-name validation. Names must match /^[A-Za-z0-9][A-Za-z0-9._-]*$/.
|
|
4
|
-
* Duplicate names within one task body raise
|
|
4
|
+
* Duplicate names within one task body raise DuplicateStepError;
|
|
5
5
|
* see ../src/index.ts.
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
package/dist/src/schema.js
CHANGED
|
@@ -42,7 +42,7 @@ async function bootstrap(db) {
|
|
|
42
42
|
if (existing) {
|
|
43
43
|
const row = await db.get("SELECT value FROM mundane_meta WHERE key='schema_version'");
|
|
44
44
|
if (row && row.value !== exports.SCHEMA_VERSION) {
|
|
45
|
-
throw new errors_1.
|
|
45
|
+
throw new errors_1.SchemaError(`schema_version is ${JSON.stringify(row.value)}, expected "${exports.SCHEMA_VERSION}"`);
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
await db.exec("BEGIN IMMEDIATE");
|
|
@@ -50,15 +50,13 @@ async function bootstrap(db) {
|
|
|
50
50
|
await db.exec(exports.CREATE_META);
|
|
51
51
|
await db.exec(exports.CREATE_STEPS);
|
|
52
52
|
await db.exec(exports.CREATE_INDEX);
|
|
53
|
-
|
|
54
|
-
exports.SCHEMA_VERSION,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
new Date().toISOString(),
|
|
61
|
-
]);
|
|
53
|
+
for (const [key, value] of [
|
|
54
|
+
["schema_version", exports.SCHEMA_VERSION],
|
|
55
|
+
["task_id", (0, node_crypto_1.randomUUID)()],
|
|
56
|
+
["created_at", new Date().toISOString()],
|
|
57
|
+
]) {
|
|
58
|
+
await db.run("INSERT OR IGNORE INTO mundane_meta (key, value) VALUES (?, ?)", [key, value]);
|
|
59
|
+
}
|
|
62
60
|
await db.exec("COMMIT");
|
|
63
61
|
}
|
|
64
62
|
catch (e) {
|
|
@@ -71,6 +69,6 @@ async function bootstrap(db) {
|
|
|
71
69
|
// Final check.
|
|
72
70
|
const row = await db.get("SELECT value FROM mundane_meta WHERE key='schema_version'");
|
|
73
71
|
if (!row || row.value !== exports.SCHEMA_VERSION) {
|
|
74
|
-
throw new errors_1.
|
|
72
|
+
throw new errors_1.SchemaError(`schema_version is ${JSON.stringify(row?.value)}, expected "${exports.SCHEMA_VERSION}"`);
|
|
75
73
|
}
|
|
76
74
|
}
|
package/dist/test/basic.test.js
CHANGED
|
@@ -110,13 +110,13 @@ function newDb() {
|
|
|
110
110
|
cleanup();
|
|
111
111
|
}
|
|
112
112
|
});
|
|
113
|
-
(0, node_test_1.test)("duplicate step name raises
|
|
113
|
+
(0, node_test_1.test)("duplicate step name raises DuplicateStepError", async () => {
|
|
114
114
|
const { path, cleanup } = newDb();
|
|
115
115
|
try {
|
|
116
116
|
await strict_1.default.rejects((0, index_1.run)(path, async (ctx) => {
|
|
117
117
|
await ctx.step("x", async () => 1);
|
|
118
118
|
await ctx.step("x", async () => 2);
|
|
119
|
-
}), (e) => e instanceof index_1.
|
|
119
|
+
}), (e) => e instanceof index_1.DuplicateStepError && e.stepName === "x");
|
|
120
120
|
// First step still committed before the dup raised.
|
|
121
121
|
const names = (await readSteps(path)).map((s) => s.name);
|
|
122
122
|
strict_1.default.deepEqual(names, ["x"]);
|
|
@@ -166,7 +166,7 @@ function newDb() {
|
|
|
166
166
|
const { path, cleanup } = newDb();
|
|
167
167
|
try {
|
|
168
168
|
// Back-to-back runs: if release() resolved before the helper actually
|
|
169
|
-
// dropped the flock, a later run would throw
|
|
169
|
+
// dropped the flock, a later run would throw LockedError.
|
|
170
170
|
for (let i = 0; i < 5; i++) {
|
|
171
171
|
await (0, index_1.run)(path, async (ctx) => ctx.step(`s${i}`, async () => i));
|
|
172
172
|
}
|
|
@@ -177,18 +177,18 @@ function newDb() {
|
|
|
177
177
|
cleanup();
|
|
178
178
|
}
|
|
179
179
|
});
|
|
180
|
-
(0, node_test_1.test)("non-JSON value raises
|
|
180
|
+
(0, node_test_1.test)("non-JSON value raises SerializationError", async () => {
|
|
181
181
|
const { path, cleanup } = newDb();
|
|
182
182
|
try {
|
|
183
183
|
await strict_1.default.rejects((0, index_1.run)(path, async (ctx) => {
|
|
184
184
|
await ctx.step("a", async () => new Date());
|
|
185
|
-
}), (e) => e instanceof index_1.
|
|
185
|
+
}), (e) => e instanceof index_1.SerializationError);
|
|
186
186
|
}
|
|
187
187
|
finally {
|
|
188
188
|
cleanup();
|
|
189
189
|
}
|
|
190
190
|
});
|
|
191
|
-
(0, node_test_1.test)("locked task throws
|
|
191
|
+
(0, node_test_1.test)("locked task throws LockedError", async () => {
|
|
192
192
|
const { path, cleanup } = newDb();
|
|
193
193
|
try {
|
|
194
194
|
let unblock;
|
|
@@ -202,7 +202,7 @@ function newDb() {
|
|
|
202
202
|
});
|
|
203
203
|
// Give the first run time to acquire the lock.
|
|
204
204
|
await new Promise((r) => setTimeout(r, 200));
|
|
205
|
-
await strict_1.default.rejects((0, index_1.run)(path, async () => { }), (e) => e instanceof index_1.
|
|
205
|
+
await strict_1.default.rejects((0, index_1.run)(path, async () => { }), (e) => e instanceof index_1.LockedError);
|
|
206
206
|
unblock();
|
|
207
207
|
await first;
|
|
208
208
|
}
|