@latysh-development/monolith 0.0.0-stage → 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +132 -2
- package/dist/brief/cli.d.ts +30 -0
- package/dist/brief/cli.d.ts.map +1 -0
- package/dist/brief/cli.js +85 -0
- package/dist/brief/collect.d.ts +17 -0
- package/dist/brief/collect.d.ts.map +1 -0
- package/dist/brief/collect.js +302 -0
- package/dist/brief/config.d.ts +40 -0
- package/dist/brief/config.d.ts.map +1 -0
- package/dist/brief/config.js +85 -0
- package/dist/brief/constants.d.ts +10 -0
- package/dist/brief/constants.d.ts.map +1 -0
- package/dist/brief/constants.js +9 -0
- package/dist/brief/docs.d.ts +53 -0
- package/dist/brief/docs.d.ts.map +1 -0
- package/dist/brief/docs.js +229 -0
- package/dist/brief/freshness.d.ts +104 -0
- package/dist/brief/freshness.d.ts.map +1 -0
- package/dist/brief/freshness.js +263 -0
- package/dist/brief/git-signals.d.ts +17 -0
- package/dist/brief/git-signals.d.ts.map +1 -0
- package/dist/brief/git-signals.js +101 -0
- package/dist/brief/index.d.ts +11 -0
- package/dist/brief/index.d.ts.map +1 -0
- package/dist/brief/index.js +5 -0
- package/dist/brief/reconcile.d.ts +75 -0
- package/dist/brief/reconcile.d.ts.map +1 -0
- package/dist/brief/reconcile.js +259 -0
- package/dist/brief/render.d.ts +60 -0
- package/dist/brief/render.d.ts.map +1 -0
- package/dist/brief/render.js +407 -0
- package/dist/brief/sanitize.d.ts +38 -0
- package/dist/brief/sanitize.d.ts.map +1 -0
- package/dist/brief/sanitize.js +48 -0
- package/dist/brief/selectors.d.ts +36 -0
- package/dist/brief/selectors.d.ts.map +1 -0
- package/dist/brief/selectors.js +75 -0
- package/dist/brief/story.d.ts +18 -0
- package/dist/brief/story.d.ts.map +1 -0
- package/dist/brief/story.js +59 -0
- package/dist/brief/types.d.ts +110 -0
- package/dist/brief/types.d.ts.map +1 -0
- package/dist/brief/types.js +11 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +166 -0
- package/dist/cli/init-args.d.ts +16 -0
- package/dist/cli/init-args.d.ts.map +1 -0
- package/dist/cli/init-args.js +24 -0
- package/dist/cli/ui-args.d.ts +23 -0
- package/dist/cli/ui-args.d.ts.map +1 -0
- package/dist/cli/ui-args.js +66 -0
- package/dist/cli/ui.d.ts +40 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +180 -0
- package/dist/coordinatorium/checkpoint.d.ts +44 -0
- package/dist/coordinatorium/checkpoint.d.ts.map +1 -0
- package/dist/coordinatorium/checkpoint.js +124 -0
- package/dist/coordinatorium/cli.d.ts +39 -0
- package/dist/coordinatorium/cli.d.ts.map +1 -0
- package/dist/coordinatorium/cli.js +203 -0
- package/dist/coordinatorium/errors.d.ts +112 -0
- package/dist/coordinatorium/errors.d.ts.map +1 -0
- package/dist/coordinatorium/errors.js +145 -0
- package/dist/coordinatorium/index.d.ts +11 -0
- package/dist/coordinatorium/index.d.ts.map +1 -0
- package/dist/coordinatorium/index.js +5 -0
- package/dist/coordinatorium/lock.d.ts +187 -0
- package/dist/coordinatorium/lock.d.ts.map +1 -0
- package/dist/coordinatorium/lock.js +659 -0
- package/dist/coordinatorium/mission.d.ts +32 -0
- package/dist/coordinatorium/mission.d.ts.map +1 -0
- package/dist/coordinatorium/mission.js +87 -0
- package/dist/coordinatorium/state-keeper.d.ts +115 -0
- package/dist/coordinatorium/state-keeper.d.ts.map +1 -0
- package/dist/coordinatorium/state-keeper.js +396 -0
- package/dist/coordinatorium/types.d.ts +69 -0
- package/dist/coordinatorium/types.d.ts.map +1 -0
- package/dist/coordinatorium/types.js +27 -0
- package/dist/durable/assess.d.ts +105 -0
- package/dist/durable/assess.d.ts.map +1 -0
- package/dist/durable/assess.js +239 -0
- package/dist/durable/canonical.d.ts +25 -0
- package/dist/durable/canonical.d.ts.map +1 -0
- package/dist/durable/canonical.js +78 -0
- package/dist/durable/cli.d.ts +37 -0
- package/dist/durable/cli.d.ts.map +1 -0
- package/dist/durable/cli.js +141 -0
- package/dist/durable/errors.d.ts +188 -0
- package/dist/durable/errors.d.ts.map +1 -0
- package/dist/durable/errors.js +317 -0
- package/dist/durable/fs-store.d.ts +233 -0
- package/dist/durable/fs-store.d.ts.map +1 -0
- package/dist/durable/fs-store.js +1393 -0
- package/dist/durable/index.d.ts +20 -0
- package/dist/durable/index.d.ts.map +1 -0
- package/dist/durable/index.js +13 -0
- package/dist/durable/log.d.ts +33 -0
- package/dist/durable/log.d.ts.map +1 -0
- package/dist/durable/log.js +157 -0
- package/dist/durable/memory-store.d.ts +51 -0
- package/dist/durable/memory-store.d.ts.map +1 -0
- package/dist/durable/memory-store.js +302 -0
- package/dist/durable/projection.d.ts +35 -0
- package/dist/durable/projection.d.ts.map +1 -0
- package/dist/durable/projection.js +328 -0
- package/dist/durable/prune-check.d.ts +17 -0
- package/dist/durable/prune-check.d.ts.map +1 -0
- package/dist/durable/prune-check.js +45 -0
- package/dist/durable/register.d.ts +100 -0
- package/dist/durable/register.d.ts.map +1 -0
- package/dist/durable/register.js +532 -0
- package/dist/durable/retention.d.ts +49 -0
- package/dist/durable/retention.d.ts.map +1 -0
- package/dist/durable/retention.js +59 -0
- package/dist/durable/runtime.d.ts +497 -0
- package/dist/durable/runtime.d.ts.map +1 -0
- package/dist/durable/runtime.js +1271 -0
- package/dist/durable/seams.d.ts +40 -0
- package/dist/durable/seams.d.ts.map +1 -0
- package/dist/durable/seams.js +21 -0
- package/dist/durable/testing.d.ts +11 -0
- package/dist/durable/testing.d.ts.map +1 -0
- package/dist/durable/testing.js +25 -0
- package/dist/durable/types.d.ts +442 -0
- package/dist/durable/types.d.ts.map +1 -0
- package/dist/durable/types.js +75 -0
- package/dist/graph/builder.d.ts +11 -0
- package/dist/graph/builder.d.ts.map +1 -0
- package/dist/graph/builder.js +153 -0
- package/dist/graph/identity.d.ts +37 -0
- package/dist/graph/identity.d.ts.map +1 -0
- package/dist/graph/identity.js +46 -0
- package/dist/graph/index.d.ts +7 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +3 -0
- package/dist/graph/resolver.d.ts +41 -0
- package/dist/graph/resolver.d.ts.map +1 -0
- package/dist/graph/resolver.js +36 -0
- package/dist/graph/types.d.ts +42 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph/types.js +1 -0
- package/dist/http/index.d.ts +5 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +2 -0
- package/dist/http/server.d.ts +76 -0
- package/dist/http/server.d.ts.map +1 -0
- package/dist/http/server.js +195 -0
- package/dist/http/ui-assets.d.ts +22 -0
- package/dist/http/ui-assets.d.ts.map +1 -0
- package/dist/http/ui-assets.js +100 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/init/index.d.ts +12 -0
- package/dist/init/index.d.ts.map +1 -0
- package/dist/init/index.js +90 -0
- package/dist/init/prompts.d.ts +3 -0
- package/dist/init/prompts.d.ts.map +1 -0
- package/dist/init/prompts.js +47 -0
- package/dist/init/scaffold.d.ts +25 -0
- package/dist/init/scaffold.d.ts.map +1 -0
- package/dist/init/scaffold.js +204 -0
- package/dist/init/types.d.ts +14 -0
- package/dist/init/types.d.ts.map +1 -0
- package/dist/init/types.js +1 -0
- package/dist/readmodel/dashboard.d.ts +40 -0
- package/dist/readmodel/dashboard.d.ts.map +1 -0
- package/dist/readmodel/dashboard.js +236 -0
- package/dist/readmodel/index.d.ts +3 -0
- package/dist/readmodel/index.d.ts.map +1 -0
- package/dist/readmodel/index.js +1 -0
- package/dist/readmodel/types.d.ts +104 -0
- package/dist/readmodel/types.d.ts.map +1 -0
- package/dist/readmodel/types.js +15 -0
- package/dist/root/index.d.ts +179 -0
- package/dist/root/index.d.ts.map +1 -0
- package/dist/root/index.js +418 -0
- package/dist/root/safe-write.d.ts +268 -0
- package/dist/root/safe-write.d.ts.map +1 -0
- package/dist/root/safe-write.js +966 -0
- package/dist/root/write-artifacts.d.ts +20 -0
- package/dist/root/write-artifacts.d.ts.map +1 -0
- package/dist/root/write-artifacts.js +42 -0
- package/dist/scriptorium/compile.d.ts +4 -0
- package/dist/scriptorium/compile.d.ts.map +1 -0
- package/dist/scriptorium/compile.js +185 -0
- package/dist/scriptorium/frontmatter.d.ts +13 -0
- package/dist/scriptorium/frontmatter.d.ts.map +1 -0
- package/dist/scriptorium/frontmatter.js +90 -0
- package/dist/scriptorium/index.d.ts +3 -0
- package/dist/scriptorium/index.d.ts.map +1 -0
- package/dist/scriptorium/index.js +1 -0
- package/dist/scriptorium/markdown.d.ts +31 -0
- package/dist/scriptorium/markdown.d.ts.map +1 -0
- package/dist/scriptorium/markdown.js +88 -0
- package/dist/scriptorium/types.d.ts +78 -0
- package/dist/scriptorium/types.d.ts.map +1 -0
- package/dist/scriptorium/types.js +1 -0
- package/dist/types/enforcement.d.ts +11 -0
- package/dist/types/enforcement.d.ts.map +1 -0
- package/dist/types/enforcement.js +1 -0
- package/dist/types/integrity.d.ts +33 -0
- package/dist/types/integrity.d.ts.map +1 -0
- package/dist/types/integrity.js +1 -0
- package/dist/types/project-definition.d.ts +30 -0
- package/dist/types/project-definition.d.ts.map +1 -0
- package/dist/types/project-definition.js +1 -0
- package/dist/ui/assets/cosmic-glass-bg-Dwx47zPu.webp +0 -0
- package/dist/ui/assets/index-C4NhMaq7.js +28 -0
- package/dist/ui/assets/index-DMZ7ZvZY.css +1 -0
- package/dist/ui/index.html +13 -0
- package/dist/validate/index.d.ts +6 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/dist/validate/index.js +214 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +1 -0
- package/package.json +67 -4
- package/templates/.gitkeep +0 -0
- package/templates/default-instance/instruction/.cursorrules +53 -0
- package/templates/default-instance/instruction/.windsurfrules +53 -0
- package/templates/default-instance/instruction/CLAUDE.md +53 -0
- package/templates/default-instance/monolith/context/boot/HANDOFF.md +49 -0
- package/templates/default-instance/monolith/context/boot/WORK_SESSION.md +30 -0
- package/templates/default-instance/monolith/context/execution/current-state.md +30 -0
- package/templates/default-instance/monolith/context/maps/dependency-map.md +38 -0
- package/templates/default-instance/monolith/context/maps/project-map.md +85 -0
- package/templates/default-instance/monolith/context/maps/roadmap-index.md +41 -0
- package/templates/default-instance/monolith/context/maps/routing-map.md +147 -0
- package/templates/default-instance/monolith/context/maps/semantic-map.md +73 -0
- package/templates/default-instance/monolith/context/models/model-registry.md +37 -0
- package/templates/default-instance/monolith/context/models/privacy-classification.md +35 -0
- package/templates/default-instance/monolith/context/templates/plan-template.md +56 -0
- package/templates/default-instance/monolith/governance/README.md +29 -0
- package/templates/default-instance/monolith/governance/RULE_SCHEMA.md +58 -0
- package/templates/default-instance/monolith/learning/INDEX.md +48 -0
- package/templates/default-instance/monolith/learning/injection-map.md +49 -0
- package/templates/default-instance/monolith/learning/taxonomy.md +30 -0
- package/templates/default-instance/monolith/memory/errors/README.md +26 -0
- package/templates/default-instance/monolith/memory/errors/SCHEMA.md +47 -0
- package/templates/default-instance/monolith/memory/errors/TAXONOMY.md +27 -0
- package/templates/default-instance/monolith/memory/long-term/architecture-principles.md +31 -0
- package/templates/default-instance/monolith/memory/long-term/decisions.md +36 -0
- package/templates/default-instance/monolith/memory/long-term/execution-history.md +20 -0
- package/templates/default-instance/monolith/memory/long-term/tech-debt.md +25 -0
- package/templates/default-instance/monolith/memory/working/active.md +41 -0
- package/templates/default-instance/monolith/memory/working/governance-result.md +0 -0
- package/templates/default-instance/monolith/memory/working/integrity-result.md +0 -0
- package/templates/default-instance/monolith/memory/working/story-index.md +28 -0
- package/templates/default-instance/monolith/protocols/boot/boot-sequence.md +59 -0
- package/templates/default-instance/monolith/protocols/cognition/cognitive-modes.md +90 -0
- package/templates/default-instance/monolith/protocols/cognition/dry-run.md +56 -0
- package/templates/default-instance/monolith/protocols/cognition/execution-trace-format.md +52 -0
- package/templates/default-instance/monolith/protocols/cognition/semantic-planning.md +51 -0
- package/templates/default-instance/monolith/protocols/execution/hybrid-execution.md +37 -0
- package/templates/default-instance/monolith/protocols/execution/local-llm-governance.md +42 -0
- package/templates/default-instance/monolith/protocols/governance/command-policy.md +51 -0
- package/templates/default-instance/monolith/protocols/governance/core-invariants.md +40 -0
- package/templates/default-instance/monolith/protocols/governance/memory-governance.md +54 -0
- package/templates/default-instance/monolith/protocols/governance/metadata-schema.md +62 -0
- package/templates/default-instance/monolith/protocols/governance/skills.md +53 -0
- package/templates/default-instance/monolith/protocols/governance/work-session.md +46 -0
- package/templates/default-instance/monolith/skills/INDEX.md +28 -0
- package/templates/default-instance/monolith/skills/current-status/SKILL.md +47 -0
- package/templates/default-instance/monolith/skills/finalize-story/SKILL.md +86 -0
- package/templates/default-instance/monolith/skills/global-status/SKILL.md +56 -0
- package/templates/default-instance/monolith/skills/self-improvement/SKILL.md +77 -0
- package/templates/default-instance/monolith/state.json +11 -0
- package/templates/default-instance/monolith/tests/INDEX.md +57 -0
- package/templates/default-instance/monolith/tests/scenarios/architecture-review.md +32 -0
- package/templates/default-instance/monolith/tests/scenarios/debug-failure.md +32 -0
- package/templates/default-instance/monolith/tests/scenarios/dry-run-activation.md +33 -0
- package/templates/default-instance/monolith/tests/scenarios/execution-trace-verification.md +31 -0
- package/templates/default-instance/monolith/tests/scenarios/finalize-story.md +35 -0
- package/templates/default-instance/monolith/tests/scenarios/implement-feature.md +36 -0
- package/templates/default-instance/monolith/tests/scenarios/local-llm-delegation.md +31 -0
- package/templates/default-instance/monolith/tests/scenarios/planning-request.md +32 -0
- package/templates/default-instance/monolith/tests/scenarios/sensitive-context-task.md +30 -0
- package/templates/default-instance/monolith/tests/scenarios/validator-verification.md +33 -0
- package/templates/default-instance/monolith/tests/validation/expected-behavior.md +70 -0
- package/templates/default-instance/monolith/tests/validators/INDEX.md +17 -0
- package/templates/default-instance/monolith/tests/validators/delegation-semantics.md +22 -0
- package/templates/default-instance/monolith/tests/validators/mode-consistency.md +25 -0
- package/templates/default-instance/monolith/tests/validators/privacy-routing.md +23 -0
- package/templates/default-instance/monolith/tests/validators/protocol-references.md +27 -0
- package/templates/default-instance/monolith/tests/validators/routing-integrity.md +30 -0
- package/templates/default-instance/projectroot/AGENTS.md +107 -0
- package/templates/default-instance/projectroot/monolith.config.json +32 -0
- package/templates/default-instance/projectroot/project.definition.mjs.template +41 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
/**
|
|
3
|
+
* `monolith durable ...` — read-mostly operator commands over the durable store
|
|
4
|
+
* of the current MONOLITH instance (`<monolithRoot>/durable`).
|
|
5
|
+
*
|
|
6
|
+
* monolith durable list [--status active|completed|failed|cancelled]
|
|
7
|
+
* monolith durable show <taskId> [--reveal-tokens]
|
|
8
|
+
* monolith durable assess <taskId>
|
|
9
|
+
* monolith durable packet <taskId> [--reveal-tokens]
|
|
10
|
+
* monolith durable verify [taskId]
|
|
11
|
+
* monolith durable retention --retain-days N [--compact-after N] [--apply]
|
|
12
|
+
*
|
|
13
|
+
* Output is JSON so it can be piped. Nothing here creates, claims or edits a
|
|
14
|
+
* task: tasks are driven by application code through `DurableTasks`. The only
|
|
15
|
+
* mutating command is `retention --apply`; without `--apply` it only reports.
|
|
16
|
+
*/
|
|
17
|
+
import { join } from 'path';
|
|
18
|
+
import { assertContainedFile, resolveMonolithRoot, reportWriteWarning } from '../root/index.js';
|
|
19
|
+
import { DurableError } from './errors.js';
|
|
20
|
+
import { FsEventStore } from './fs-store.js';
|
|
21
|
+
import { DurableTasks } from './runtime.js';
|
|
22
|
+
export const DURABLE_DIR_NAME = 'durable';
|
|
23
|
+
const STATUSES = ['active', 'completed', 'failed', 'cancelled'];
|
|
24
|
+
/**
|
|
25
|
+
* Await tokens are capabilities: whoever holds one can deliver a result to the task. The CLI output ends up in
|
|
26
|
+
* terminals, logs and tickets, so the secret part (the nonce) is replaced there; `--reveal-tokens` prints it.
|
|
27
|
+
* The programmatic API (`getState`, `resumePacket`) is unchanged: a worker needs the real token to send it out.
|
|
28
|
+
*/
|
|
29
|
+
function redactTokens(v) {
|
|
30
|
+
if (Array.isArray(v))
|
|
31
|
+
return v.map(redactTokens);
|
|
32
|
+
if (v !== null && typeof v === 'object') {
|
|
33
|
+
const out = {};
|
|
34
|
+
for (const [k, x] of Object.entries(v)) {
|
|
35
|
+
out[k] = k === 'token' && typeof x === 'string' ? x.replace(/\.[0-9a-f]{32}$/, '.<redacted>') : redactTokens(x);
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
return v;
|
|
40
|
+
}
|
|
41
|
+
function flag(args, name) {
|
|
42
|
+
const i = args.indexOf(name);
|
|
43
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
44
|
+
}
|
|
45
|
+
function openStore(cwd) {
|
|
46
|
+
const root = resolveMonolithRoot(cwd);
|
|
47
|
+
assertContainedFile(root, `${DURABLE_DIR_NAME}/x`);
|
|
48
|
+
const dir = join(root.rootAbs, DURABLE_DIR_NAME);
|
|
49
|
+
return new FsEventStore({ target: { scope: 'project', projectRoot: root.projectRoot, dir }, onWarning: (w) => reportWriteWarning(w) });
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Opens the durable store of the instance. A store without `durable-protocol.json` is open for diagnostics only: reads
|
|
53
|
+
* work, every write is refused (`STORE_PROTOCOL`). Opening never creates the marker.
|
|
54
|
+
*/
|
|
55
|
+
export function openDurable(cwd) {
|
|
56
|
+
return new DurableTasks({ store: openStore(cwd) });
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The explicit operator action that makes an EMPTY store writable (Design A section 46.5, ADR-018 D3). Not reachable from
|
|
60
|
+
* `runDurable` on purpose: it must be called by a person or script that knows no runtime of an older version is running.
|
|
61
|
+
* Refuses a store that already holds tasks.
|
|
62
|
+
*/
|
|
63
|
+
export function initializeDurable(cwd) {
|
|
64
|
+
return openStore(cwd).initializeProtocolSync();
|
|
65
|
+
}
|
|
66
|
+
/** Returns the process exit code: 0 ok, 1 usage/error, 2 verification found a problem. */
|
|
67
|
+
export async function runDurable(args, io) {
|
|
68
|
+
const [sub, ...rest] = args;
|
|
69
|
+
const reveal = rest.includes('--reveal-tokens');
|
|
70
|
+
const json = (v) => io.out(JSON.stringify(reveal ? v : redactTokens(v), null, 2));
|
|
71
|
+
try {
|
|
72
|
+
const rt = openDurable(io.cwd);
|
|
73
|
+
switch (sub) {
|
|
74
|
+
case 'list': {
|
|
75
|
+
const status = flag(rest, '--status');
|
|
76
|
+
if (status !== undefined && !STATUSES.includes(status)) {
|
|
77
|
+
io.err(`--status must be one of ${STATUSES.join(', ')}.`);
|
|
78
|
+
return 1;
|
|
79
|
+
}
|
|
80
|
+
json(await rt.listTasks(status ? { status: status } : {}));
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
case 'show': {
|
|
84
|
+
if (!rest[0])
|
|
85
|
+
return usage(io, 'show <taskId>');
|
|
86
|
+
json(await rt.getState(rest[0]));
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
case 'assess': {
|
|
90
|
+
if (!rest[0])
|
|
91
|
+
return usage(io, 'assess <taskId>');
|
|
92
|
+
json(await rt.assess(rest[0]));
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
case 'packet': {
|
|
96
|
+
if (!rest[0])
|
|
97
|
+
return usage(io, 'packet <taskId>');
|
|
98
|
+
json(await rt.resumePacket(rest[0]));
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
case 'verify': {
|
|
102
|
+
const reports = await rt.verify(rest[0]);
|
|
103
|
+
json(reports);
|
|
104
|
+
return reports.every((r) => r.ok) ? 0 : 2;
|
|
105
|
+
}
|
|
106
|
+
case 'retention': {
|
|
107
|
+
const days = Number(flag(rest, '--retain-days'));
|
|
108
|
+
if (!Number.isFinite(days) || days < 0)
|
|
109
|
+
return usage(io, 'retention --retain-days N [--compact-after N] [--apply]');
|
|
110
|
+
const compact = flag(rest, '--compact-after');
|
|
111
|
+
const policy = {
|
|
112
|
+
terminalRetentionMs: days * 86_400_000,
|
|
113
|
+
...(compact !== undefined ? { compactAfterEvents: Number(compact) } : {}),
|
|
114
|
+
};
|
|
115
|
+
if (rest.includes('--apply'))
|
|
116
|
+
json(await rt.applyRetention(policy));
|
|
117
|
+
else
|
|
118
|
+
json({ dryRun: true, decisions: await rt.planRetention(policy) });
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
default:
|
|
122
|
+
return usage(io, 'list | show <id> | assess <id> | packet <id> | verify [id] | retention --retain-days N [--apply]');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
if (err instanceof DurableError)
|
|
127
|
+
io.err(`${err.code}: ${err.message}`);
|
|
128
|
+
else
|
|
129
|
+
io.err(err instanceof Error ? err.message : String(err));
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function usage(io, text) {
|
|
134
|
+
io.err(`Usage: monolith durable ${text}`);
|
|
135
|
+
return 1;
|
|
136
|
+
}
|
|
137
|
+
export async function runDurableCommand(args) {
|
|
138
|
+
const code = await runDurable(args, { cwd: process.cwd(), out: (s) => console.log(s), err: (s) => console.error(s) });
|
|
139
|
+
if (code !== 0)
|
|
140
|
+
process.exit(code);
|
|
141
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable State — typed error taxonomy.
|
|
3
|
+
*
|
|
4
|
+
* Every error carries a stable `code`. Callers branch on `code`, never on
|
|
5
|
+
* message text. No error message embeds event payloads or file contents
|
|
6
|
+
* (payloads are application data and may be sensitive).
|
|
7
|
+
*/
|
|
8
|
+
export type DurableErrorCode = 'VALIDATION' | 'TASK_NOT_FOUND' | 'TASK_PRUNED' | 'TASK_EXISTS' | 'TASK_CLOSED' | 'TASK_ORPHANED' | 'REQUIRED_TASK_PENDING' | 'LEASE_HELD' | 'STALE_FENCE' | 'CONTENTION' | 'LOG_CORRUPT' | 'LOG_UNSUPPORTED' | 'LOG_TOO_LARGE' | 'STORE_SECURITY' | 'STORE_IO' | 'EFFECT_IN_DOUBT' | 'EFFECT_FAILED' | 'EFFECT_CONFLICT' | 'EFFECT_NOT_EXECUTED' | 'RETENTION_REFUSED' | 'PRUNE_DECISION_CORRUPT' | 'PRUNE_ATTEMPT_LIMIT' | 'STORE_PROTOCOL';
|
|
9
|
+
export declare class DurableError extends Error {
|
|
10
|
+
readonly code: DurableErrorCode;
|
|
11
|
+
constructor(code: DurableErrorCode, message: string);
|
|
12
|
+
}
|
|
13
|
+
export declare class ValidationError extends DurableError {
|
|
14
|
+
constructor(message: string);
|
|
15
|
+
}
|
|
16
|
+
export declare class TaskNotFoundError extends DurableError {
|
|
17
|
+
readonly taskId: string;
|
|
18
|
+
constructor(taskId: string);
|
|
19
|
+
}
|
|
20
|
+
/** The task existed and was pruned by retention; only its tombstone remains. */
|
|
21
|
+
export declare class TaskPrunedError extends DurableError {
|
|
22
|
+
readonly taskId: string;
|
|
23
|
+
constructor(taskId: string);
|
|
24
|
+
}
|
|
25
|
+
export declare class TaskExistsError extends DurableError {
|
|
26
|
+
readonly taskId: string;
|
|
27
|
+
constructor(taskId: string);
|
|
28
|
+
}
|
|
29
|
+
/** The task reached a terminal state; its log is sealed. */
|
|
30
|
+
export declare class TaskClosedError extends DurableError {
|
|
31
|
+
readonly taskId: string;
|
|
32
|
+
readonly status: string;
|
|
33
|
+
constructor(taskId: string, status: string);
|
|
34
|
+
}
|
|
35
|
+
export declare class LeaseHeldError extends DurableError {
|
|
36
|
+
readonly taskId: string;
|
|
37
|
+
readonly owner: string;
|
|
38
|
+
readonly leaseUntil: string;
|
|
39
|
+
constructor(taskId: string, owner: string, leaseUntil: string);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A worker acted with an epoch/owner that is no longer current: someone else
|
|
43
|
+
* claimed the task (fencing). The worker must stop; nothing was written.
|
|
44
|
+
*/
|
|
45
|
+
export declare class StaleFenceError extends DurableError {
|
|
46
|
+
readonly taskId: string;
|
|
47
|
+
readonly owner: string;
|
|
48
|
+
readonly epoch: number;
|
|
49
|
+
readonly currentEpoch: number;
|
|
50
|
+
constructor(taskId: string, owner: string, epoch: number, currentEpoch: number);
|
|
51
|
+
}
|
|
52
|
+
export declare class ContentionError extends DurableError {
|
|
53
|
+
readonly taskId: string;
|
|
54
|
+
readonly attempts: number;
|
|
55
|
+
constructor(taskId: string, attempts: number);
|
|
56
|
+
}
|
|
57
|
+
/** The log failed an integrity check (hash chain, sequence gap, schema). Fail closed; never auto-repaired. */
|
|
58
|
+
export declare class LogCorruptError extends DurableError {
|
|
59
|
+
readonly taskId: string;
|
|
60
|
+
readonly seq: number | null;
|
|
61
|
+
readonly reason: string;
|
|
62
|
+
constructor(taskId: string, seq: number | null, reason: string);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* `withdrawTombstone` found the pruned task's data no longer complete: removal has started, so the prune is committed
|
|
66
|
+
* and cannot be rolled back. (It is a `LogCorruptError` for callers that only know the old contract.)
|
|
67
|
+
*/
|
|
68
|
+
export declare class TombstoneCommittedError extends LogCorruptError {
|
|
69
|
+
constructor(taskId: string, reason: string);
|
|
70
|
+
}
|
|
71
|
+
/** The log contains an event format/type this version does not know. Never skipped silently. */
|
|
72
|
+
export declare class LogUnsupportedError extends DurableError {
|
|
73
|
+
readonly taskId: string;
|
|
74
|
+
readonly seq: number;
|
|
75
|
+
readonly what: string;
|
|
76
|
+
constructor(taskId: string, seq: number, what: string);
|
|
77
|
+
}
|
|
78
|
+
export declare class LogTooLargeError extends DurableError {
|
|
79
|
+
readonly taskId: string;
|
|
80
|
+
readonly limit: number;
|
|
81
|
+
constructor(taskId: string, limit: number);
|
|
82
|
+
}
|
|
83
|
+
/** The filesystem layout is not what a managed store must look like (symlink, foreign hard link, special file). */
|
|
84
|
+
export declare class StoreSecurityError extends DurableError {
|
|
85
|
+
readonly detailCode: string;
|
|
86
|
+
constructor(message: string, detailCode: string);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The storage layer failed (disk full, I/O error, permission). Nothing was published by the failed call.
|
|
90
|
+
* This is a PHYSICAL failure and is never confused with `LogTooLargeError`, which is the logical event budget.
|
|
91
|
+
* `errnoCode` is the operating-system code found in the cause chain (`ENOSPC`, `EDQUOT`, `EIO`, ...), if any.
|
|
92
|
+
*/
|
|
93
|
+
export declare class StoreIoError extends DurableError {
|
|
94
|
+
readonly cause?: unknown | undefined;
|
|
95
|
+
readonly errnoCode: string | undefined;
|
|
96
|
+
constructor(message: string, cause?: unknown | undefined);
|
|
97
|
+
}
|
|
98
|
+
/** An effect was announced but its outcome was never recorded (crash between intent and result). */
|
|
99
|
+
export declare class EffectInDoubtError extends DurableError {
|
|
100
|
+
readonly taskId: string;
|
|
101
|
+
readonly effectKey: string;
|
|
102
|
+
constructor(taskId: string, effectKey: string);
|
|
103
|
+
}
|
|
104
|
+
export declare class EffectFailedError extends DurableError {
|
|
105
|
+
readonly taskId: string;
|
|
106
|
+
readonly effectKey: string;
|
|
107
|
+
readonly detail: string;
|
|
108
|
+
constructor(taskId: string, effectKey: string, detail: string);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Thrown by the function given to `runEffect` to assert that the external operation did NOT take effect
|
|
112
|
+
* (for example a connection refused before any request was sent). It is the only kind of error that leaves
|
|
113
|
+
* an effect safely retryable by default: any other exception, a timeout above all, proves nothing about
|
|
114
|
+
* what the remote side did, and the effect is then recorded as having an unknown outcome.
|
|
115
|
+
*/
|
|
116
|
+
export declare class EffectNotExecutedError extends DurableError {
|
|
117
|
+
readonly cause?: unknown | undefined;
|
|
118
|
+
constructor(message: string, cause?: unknown | undefined);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Another caller announced, finished or re-announced the same effect between this caller's decision and its
|
|
122
|
+
* announcement (compare-and-set on the effect's identity in the log lost). The function was NOT run by this call.
|
|
123
|
+
* Re-read the task and decide again; the other caller's attempt is the one that counts.
|
|
124
|
+
*/
|
|
125
|
+
export declare class EffectConflictError extends DurableError {
|
|
126
|
+
readonly taskId: string;
|
|
127
|
+
readonly effectKey: string;
|
|
128
|
+
constructor(taskId: string, effectKey: string);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The task is active in the log, but a task it requires (`parentTaskId` / `dependsOn`) was pruned or does not exist,
|
|
132
|
+
* so it must not run. Raised by `claim`; the log is NOT what decides this, the required task's state right now is.
|
|
133
|
+
*/
|
|
134
|
+
export declare class TaskOrphanedError extends DurableError {
|
|
135
|
+
readonly taskId: string;
|
|
136
|
+
readonly requiredTaskId: string;
|
|
137
|
+
constructor(taskId: string, requiredTaskId: string);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* A task this one requires is in an unfinished prune (tombstone published, removal not finished or not provably
|
|
141
|
+
* rolled back), or could not be examined right now. Nothing is run and NOTHING IS CANCELLED: the state is transient,
|
|
142
|
+
* and the call can simply be repeated. Contrast `TaskOrphanedError`, which is final.
|
|
143
|
+
*/
|
|
144
|
+
export declare class RequiredTaskPendingError extends DurableError {
|
|
145
|
+
readonly taskId: string;
|
|
146
|
+
readonly requiredTaskId: string;
|
|
147
|
+
readonly retryable = true;
|
|
148
|
+
constructor(taskId: string, requiredTaskId: string);
|
|
149
|
+
}
|
|
150
|
+
export declare class RetentionRefusedError extends DurableError {
|
|
151
|
+
readonly taskId: string;
|
|
152
|
+
readonly why: string;
|
|
153
|
+
constructor(taskId: string, why: string);
|
|
154
|
+
}
|
|
155
|
+
/** Why the prune decision register of a task cannot be trusted (Design A, section 20.4). */
|
|
156
|
+
export type PruneCorruptReason = 'register' | 'gap' | 'unknown-artifact' | 'commit-audit' | 'multiple-commit' | 'history' | 'task-dir-missing' | 'tombstone-without-commit' | 'legacy-tombstone-unmigrated';
|
|
157
|
+
/**
|
|
158
|
+
* The persisted prune decision register of a task is corrupt, or the task directory is missing, or it is an
|
|
159
|
+
* unmigrated legacy tombstone. Fail closed: nothing is deleted and nobody is cancelled; an operator decides.
|
|
160
|
+
* Never mapped to `TaskOrphanedError`, `RetentionRefusedError` or a quarantine, and never retryable.
|
|
161
|
+
* `published` is true when a write the caller made is already visible (the outcome of that write is unknown).
|
|
162
|
+
*/
|
|
163
|
+
export declare class PruneDecisionCorruptError extends DurableError {
|
|
164
|
+
readonly taskId: string;
|
|
165
|
+
readonly reason: PruneCorruptReason;
|
|
166
|
+
readonly file: string | null;
|
|
167
|
+
readonly attempt: number | null;
|
|
168
|
+
readonly published: boolean;
|
|
169
|
+
readonly retryable = false;
|
|
170
|
+
constructor(taskId: string, reason: PruneCorruptReason, file?: string | null, attempt?: number | null, published?: boolean);
|
|
171
|
+
}
|
|
172
|
+
/** The attempt number would exceed `MAX_PRUNE_ATTEMPTS`. A liveness limit: numbers are never reused or reset. */
|
|
173
|
+
export declare class PruneAttemptLimitError extends DurableError {
|
|
174
|
+
readonly taskId: string;
|
|
175
|
+
readonly attempts: number;
|
|
176
|
+
constructor(taskId: string, attempts: number);
|
|
177
|
+
}
|
|
178
|
+
export type StoreProtocolCode = 'PROTOCOL_NOT_INITIALIZED' | 'PROTOCOL_UNSUPPORTED' | 'PROTOCOL_STORE_NOT_EMPTY';
|
|
179
|
+
/**
|
|
180
|
+
* The store does not carry a usable `durable-protocol.json`: it is uninitialized (read-only diagnostics only), has a
|
|
181
|
+
* version this build does not understand, or cannot be initialized because it already holds tasks. The marker is
|
|
182
|
+
* created only by an explicit operator action, never by opening the store.
|
|
183
|
+
*/
|
|
184
|
+
export declare class StoreProtocolError extends DurableError {
|
|
185
|
+
readonly detailCode: StoreProtocolCode;
|
|
186
|
+
constructor(message: string, detailCode: StoreProtocolCode);
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/durable/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable State — typed error taxonomy.
|
|
3
|
+
*
|
|
4
|
+
* Every error carries a stable `code`. Callers branch on `code`, never on
|
|
5
|
+
* message text. No error message embeds event payloads or file contents
|
|
6
|
+
* (payloads are application data and may be sensitive).
|
|
7
|
+
*/
|
|
8
|
+
export class DurableError extends Error {
|
|
9
|
+
code;
|
|
10
|
+
constructor(code, message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'DurableError';
|
|
13
|
+
this.code = code;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class ValidationError extends DurableError {
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super('VALIDATION', message);
|
|
19
|
+
this.name = 'DurableValidationError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export class TaskNotFoundError extends DurableError {
|
|
23
|
+
taskId;
|
|
24
|
+
constructor(taskId) {
|
|
25
|
+
super('TASK_NOT_FOUND', `task "${taskId}" does not exist.`);
|
|
26
|
+
this.taskId = taskId;
|
|
27
|
+
this.name = 'TaskNotFoundError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** The task existed and was pruned by retention; only its tombstone remains. */
|
|
31
|
+
export class TaskPrunedError extends DurableError {
|
|
32
|
+
taskId;
|
|
33
|
+
constructor(taskId) {
|
|
34
|
+
super('TASK_PRUNED', `task "${taskId}" was pruned by retention; only its tombstone remains.`);
|
|
35
|
+
this.taskId = taskId;
|
|
36
|
+
this.name = 'TaskPrunedError';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export class TaskExistsError extends DurableError {
|
|
40
|
+
taskId;
|
|
41
|
+
constructor(taskId) {
|
|
42
|
+
super('TASK_EXISTS', `task "${taskId}" already exists with different content.`);
|
|
43
|
+
this.taskId = taskId;
|
|
44
|
+
this.name = 'TaskExistsError';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** The task reached a terminal state; its log is sealed. */
|
|
48
|
+
export class TaskClosedError extends DurableError {
|
|
49
|
+
taskId;
|
|
50
|
+
status;
|
|
51
|
+
constructor(taskId, status) {
|
|
52
|
+
super('TASK_CLOSED', `task "${taskId}" is ${status}; its log is sealed.`);
|
|
53
|
+
this.taskId = taskId;
|
|
54
|
+
this.status = status;
|
|
55
|
+
this.name = 'TaskClosedError';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export class LeaseHeldError extends DurableError {
|
|
59
|
+
taskId;
|
|
60
|
+
owner;
|
|
61
|
+
leaseUntil;
|
|
62
|
+
constructor(taskId, owner, leaseUntil) {
|
|
63
|
+
super('LEASE_HELD', `task "${taskId}" is leased by "${owner}" until ${leaseUntil}.`);
|
|
64
|
+
this.taskId = taskId;
|
|
65
|
+
this.owner = owner;
|
|
66
|
+
this.leaseUntil = leaseUntil;
|
|
67
|
+
this.name = 'LeaseHeldError';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A worker acted with an epoch/owner that is no longer current: someone else
|
|
72
|
+
* claimed the task (fencing). The worker must stop; nothing was written.
|
|
73
|
+
*/
|
|
74
|
+
export class StaleFenceError extends DurableError {
|
|
75
|
+
taskId;
|
|
76
|
+
owner;
|
|
77
|
+
epoch;
|
|
78
|
+
currentEpoch;
|
|
79
|
+
constructor(taskId, owner, epoch, currentEpoch) {
|
|
80
|
+
super('STALE_FENCE', `"${owner}" (epoch ${epoch}) no longer holds task "${taskId}" (current epoch ${currentEpoch}).`);
|
|
81
|
+
this.taskId = taskId;
|
|
82
|
+
this.owner = owner;
|
|
83
|
+
this.epoch = epoch;
|
|
84
|
+
this.currentEpoch = currentEpoch;
|
|
85
|
+
this.name = 'StaleFenceError';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export class ContentionError extends DurableError {
|
|
89
|
+
taskId;
|
|
90
|
+
attempts;
|
|
91
|
+
constructor(taskId, attempts) {
|
|
92
|
+
super('CONTENTION', `gave up appending to task "${taskId}" after ${attempts} lost races.`);
|
|
93
|
+
this.taskId = taskId;
|
|
94
|
+
this.attempts = attempts;
|
|
95
|
+
this.name = 'ContentionError';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** The log failed an integrity check (hash chain, sequence gap, schema). Fail closed; never auto-repaired. */
|
|
99
|
+
export class LogCorruptError extends DurableError {
|
|
100
|
+
taskId;
|
|
101
|
+
seq;
|
|
102
|
+
reason;
|
|
103
|
+
constructor(taskId, seq, reason) {
|
|
104
|
+
super('LOG_CORRUPT', `log of task "${taskId}" is corrupt${seq === null ? '' : ` at seq ${seq}`}: ${reason}`);
|
|
105
|
+
this.taskId = taskId;
|
|
106
|
+
this.seq = seq;
|
|
107
|
+
this.reason = reason;
|
|
108
|
+
this.name = 'LogCorruptError';
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* `withdrawTombstone` found the pruned task's data no longer complete: removal has started, so the prune is committed
|
|
113
|
+
* and cannot be rolled back. (It is a `LogCorruptError` for callers that only know the old contract.)
|
|
114
|
+
*/
|
|
115
|
+
export class TombstoneCommittedError extends LogCorruptError {
|
|
116
|
+
constructor(taskId, reason) {
|
|
117
|
+
super(taskId, null, reason);
|
|
118
|
+
this.name = 'TombstoneCommittedError';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** The log contains an event format/type this version does not know. Never skipped silently. */
|
|
122
|
+
export class LogUnsupportedError extends DurableError {
|
|
123
|
+
taskId;
|
|
124
|
+
seq;
|
|
125
|
+
what;
|
|
126
|
+
constructor(taskId, seq, what) {
|
|
127
|
+
super('LOG_UNSUPPORTED', `log of task "${taskId}" has an unsupported ${what} at seq ${seq}; refusing to interpret it.`);
|
|
128
|
+
this.taskId = taskId;
|
|
129
|
+
this.seq = seq;
|
|
130
|
+
this.what = what;
|
|
131
|
+
this.name = 'LogUnsupportedError';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export class LogTooLargeError extends DurableError {
|
|
135
|
+
taskId;
|
|
136
|
+
limit;
|
|
137
|
+
constructor(taskId, limit) {
|
|
138
|
+
super('LOG_TOO_LARGE', `task "${taskId}" reached the event limit (${limit}); continue the work in a new task (parentTaskId). ` +
|
|
139
|
+
'Completing, failing or cancelling the task is still possible.');
|
|
140
|
+
this.taskId = taskId;
|
|
141
|
+
this.limit = limit;
|
|
142
|
+
this.name = 'LogTooLargeError';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/** The filesystem layout is not what a managed store must look like (symlink, foreign hard link, special file). */
|
|
146
|
+
export class StoreSecurityError extends DurableError {
|
|
147
|
+
detailCode;
|
|
148
|
+
constructor(message, detailCode) {
|
|
149
|
+
super('STORE_SECURITY', message);
|
|
150
|
+
this.detailCode = detailCode;
|
|
151
|
+
this.name = 'StoreSecurityError';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The storage layer failed (disk full, I/O error, permission). Nothing was published by the failed call.
|
|
156
|
+
* This is a PHYSICAL failure and is never confused with `LogTooLargeError`, which is the logical event budget.
|
|
157
|
+
* `errnoCode` is the operating-system code found in the cause chain (`ENOSPC`, `EDQUOT`, `EIO`, ...), if any.
|
|
158
|
+
*/
|
|
159
|
+
export class StoreIoError extends DurableError {
|
|
160
|
+
cause;
|
|
161
|
+
errnoCode;
|
|
162
|
+
constructor(message, cause) {
|
|
163
|
+
super('STORE_IO', message);
|
|
164
|
+
this.cause = cause;
|
|
165
|
+
this.name = 'StoreIoError';
|
|
166
|
+
this.errnoCode = errnoOf(cause);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function errnoOf(cause) {
|
|
170
|
+
let c = cause;
|
|
171
|
+
for (let i = 0; i < 4 && c !== null && typeof c === 'object'; i++) {
|
|
172
|
+
const code = c.code;
|
|
173
|
+
if (typeof code === 'string' && /^E[A-Z0-9]{2,}$/.test(code))
|
|
174
|
+
return code;
|
|
175
|
+
c = c.cause;
|
|
176
|
+
}
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
/** An effect was announced but its outcome was never recorded (crash between intent and result). */
|
|
180
|
+
export class EffectInDoubtError extends DurableError {
|
|
181
|
+
taskId;
|
|
182
|
+
effectKey;
|
|
183
|
+
constructor(taskId, effectKey) {
|
|
184
|
+
super('EFFECT_IN_DOUBT', `effect "${effectKey}" of task "${taskId}" has an unrecorded outcome; reconcile it against the external system.`);
|
|
185
|
+
this.taskId = taskId;
|
|
186
|
+
this.effectKey = effectKey;
|
|
187
|
+
this.name = 'EffectInDoubtError';
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
export class EffectFailedError extends DurableError {
|
|
191
|
+
taskId;
|
|
192
|
+
effectKey;
|
|
193
|
+
detail;
|
|
194
|
+
constructor(taskId, effectKey, detail) {
|
|
195
|
+
super('EFFECT_FAILED', `effect "${effectKey}" of task "${taskId}" failed permanently: ${detail}`);
|
|
196
|
+
this.taskId = taskId;
|
|
197
|
+
this.effectKey = effectKey;
|
|
198
|
+
this.detail = detail;
|
|
199
|
+
this.name = 'EffectFailedError';
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Thrown by the function given to `runEffect` to assert that the external operation did NOT take effect
|
|
204
|
+
* (for example a connection refused before any request was sent). It is the only kind of error that leaves
|
|
205
|
+
* an effect safely retryable by default: any other exception, a timeout above all, proves nothing about
|
|
206
|
+
* what the remote side did, and the effect is then recorded as having an unknown outcome.
|
|
207
|
+
*/
|
|
208
|
+
export class EffectNotExecutedError extends DurableError {
|
|
209
|
+
cause;
|
|
210
|
+
constructor(message, cause) {
|
|
211
|
+
super('EFFECT_NOT_EXECUTED', message);
|
|
212
|
+
this.cause = cause;
|
|
213
|
+
this.name = 'EffectNotExecutedError';
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Another caller announced, finished or re-announced the same effect between this caller's decision and its
|
|
218
|
+
* announcement (compare-and-set on the effect's identity in the log lost). The function was NOT run by this call.
|
|
219
|
+
* Re-read the task and decide again; the other caller's attempt is the one that counts.
|
|
220
|
+
*/
|
|
221
|
+
export class EffectConflictError extends DurableError {
|
|
222
|
+
taskId;
|
|
223
|
+
effectKey;
|
|
224
|
+
constructor(taskId, effectKey) {
|
|
225
|
+
super('EFFECT_CONFLICT', `effect "${effectKey}" of task "${taskId}" was changed by another caller while this call was deciding; it was not run by this call.`);
|
|
226
|
+
this.taskId = taskId;
|
|
227
|
+
this.effectKey = effectKey;
|
|
228
|
+
this.name = 'EffectConflictError';
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* The task is active in the log, but a task it requires (`parentTaskId` / `dependsOn`) was pruned or does not exist,
|
|
233
|
+
* so it must not run. Raised by `claim`; the log is NOT what decides this, the required task's state right now is.
|
|
234
|
+
*/
|
|
235
|
+
export class TaskOrphanedError extends DurableError {
|
|
236
|
+
taskId;
|
|
237
|
+
requiredTaskId;
|
|
238
|
+
constructor(taskId, requiredTaskId) {
|
|
239
|
+
super('TASK_ORPHANED', `task "${taskId}" requires task "${requiredTaskId}", which was pruned or does not exist; it will not be run.`);
|
|
240
|
+
this.taskId = taskId;
|
|
241
|
+
this.requiredTaskId = requiredTaskId;
|
|
242
|
+
this.name = 'TaskOrphanedError';
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* A task this one requires is in an unfinished prune (tombstone published, removal not finished or not provably
|
|
247
|
+
* rolled back), or could not be examined right now. Nothing is run and NOTHING IS CANCELLED: the state is transient,
|
|
248
|
+
* and the call can simply be repeated. Contrast `TaskOrphanedError`, which is final.
|
|
249
|
+
*/
|
|
250
|
+
export class RequiredTaskPendingError extends DurableError {
|
|
251
|
+
taskId;
|
|
252
|
+
requiredTaskId;
|
|
253
|
+
retryable = true;
|
|
254
|
+
constructor(taskId, requiredTaskId) {
|
|
255
|
+
super('REQUIRED_TASK_PENDING', `task "${taskId}" requires task "${requiredTaskId}", whose prune is not settled; it is not run now, and not cancelled. Retry.`);
|
|
256
|
+
this.taskId = taskId;
|
|
257
|
+
this.requiredTaskId = requiredTaskId;
|
|
258
|
+
this.name = 'RequiredTaskPendingError';
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
export class RetentionRefusedError extends DurableError {
|
|
262
|
+
taskId;
|
|
263
|
+
why;
|
|
264
|
+
constructor(taskId, why) {
|
|
265
|
+
super('RETENTION_REFUSED', `refusing to prune task "${taskId}": ${why}`);
|
|
266
|
+
this.taskId = taskId;
|
|
267
|
+
this.why = why;
|
|
268
|
+
this.name = 'RetentionRefusedError';
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* The persisted prune decision register of a task is corrupt, or the task directory is missing, or it is an
|
|
273
|
+
* unmigrated legacy tombstone. Fail closed: nothing is deleted and nobody is cancelled; an operator decides.
|
|
274
|
+
* Never mapped to `TaskOrphanedError`, `RetentionRefusedError` or a quarantine, and never retryable.
|
|
275
|
+
* `published` is true when a write the caller made is already visible (the outcome of that write is unknown).
|
|
276
|
+
*/
|
|
277
|
+
export class PruneDecisionCorruptError extends DurableError {
|
|
278
|
+
taskId;
|
|
279
|
+
reason;
|
|
280
|
+
file;
|
|
281
|
+
attempt;
|
|
282
|
+
published;
|
|
283
|
+
retryable = false;
|
|
284
|
+
constructor(taskId, reason, file = null, attempt = null, published = false) {
|
|
285
|
+
super('PRUNE_DECISION_CORRUPT', `the prune decision register of task "${taskId}" cannot be trusted (${reason}${file ? `: ${file}` : ''}); nothing was deleted or cancelled.`);
|
|
286
|
+
this.taskId = taskId;
|
|
287
|
+
this.reason = reason;
|
|
288
|
+
this.file = file;
|
|
289
|
+
this.attempt = attempt;
|
|
290
|
+
this.published = published;
|
|
291
|
+
this.name = 'PruneDecisionCorruptError';
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/** The attempt number would exceed `MAX_PRUNE_ATTEMPTS`. A liveness limit: numbers are never reused or reset. */
|
|
295
|
+
export class PruneAttemptLimitError extends DurableError {
|
|
296
|
+
taskId;
|
|
297
|
+
attempts;
|
|
298
|
+
constructor(taskId, attempts) {
|
|
299
|
+
super('PRUNE_ATTEMPT_LIMIT', `task "${taskId}" has used all ${attempts} prune attempts; raise the limit or find out why they keep being aborted.`);
|
|
300
|
+
this.taskId = taskId;
|
|
301
|
+
this.attempts = attempts;
|
|
302
|
+
this.name = 'PruneAttemptLimitError';
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* The store does not carry a usable `durable-protocol.json`: it is uninitialized (read-only diagnostics only), has a
|
|
307
|
+
* version this build does not understand, or cannot be initialized because it already holds tasks. The marker is
|
|
308
|
+
* created only by an explicit operator action, never by opening the store.
|
|
309
|
+
*/
|
|
310
|
+
export class StoreProtocolError extends DurableError {
|
|
311
|
+
detailCode;
|
|
312
|
+
constructor(message, detailCode) {
|
|
313
|
+
super('STORE_PROTOCOL', message);
|
|
314
|
+
this.detailCode = detailCode;
|
|
315
|
+
this.name = 'StoreProtocolError';
|
|
316
|
+
}
|
|
317
|
+
}
|