@latysh-development/monolith 0.0.0-stage → 0.1.0-alpha.1
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 +179 -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 +342 -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 +12 -0
- package/dist/brief/constants.d.ts.map +1 -0
- package/dist/brief/constants.js +11 -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 +67 -0
- package/dist/brief/render.d.ts.map +1 -0
- package/dist/brief/render.js +458 -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 +126 -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 +199 -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/discovery/compose.d.ts +28 -0
- package/dist/discovery/compose.d.ts.map +1 -0
- package/dist/discovery/compose.js +159 -0
- package/dist/discovery/contracts.d.ts +211 -0
- package/dist/discovery/contracts.d.ts.map +1 -0
- package/dist/discovery/contracts.js +555 -0
- package/dist/discovery/docs.d.ts +278 -0
- package/dist/discovery/docs.d.ts.map +1 -0
- package/dist/discovery/docs.js +1182 -0
- package/dist/discovery/facts.d.ts +49 -0
- package/dist/discovery/facts.d.ts.map +1 -0
- package/dist/discovery/facts.js +541 -0
- package/dist/discovery/index.d.ts +8 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +9 -0
- package/dist/discovery/orchestrate.d.ts +35 -0
- package/dist/discovery/orchestrate.d.ts.map +1 -0
- package/dist/discovery/orchestrate.js +32 -0
- package/dist/discovery/render.d.ts +36 -0
- package/dist/discovery/render.d.ts.map +1 -0
- package/dist/discovery/render.js +278 -0
- package/dist/discovery/traverse.d.ts +197 -0
- package/dist/discovery/traverse.d.ts.map +1 -0
- package/dist/discovery/traverse.js +510 -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 +192 -0
- package/dist/durable/errors.d.ts.map +1 -0
- package/dist/durable/errors.js +323 -0
- package/dist/durable/fs-store.d.ts +254 -0
- package/dist/durable/fs-store.d.ts.map +1 -0
- package/dist/durable/fs-store.js +1493 -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 +34 -0
- package/dist/durable/prune-check.d.ts.map +1 -0
- package/dist/durable/prune-check.js +72 -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 +499 -0
- package/dist/durable/runtime.d.ts.map +1 -0
- package/dist/durable/runtime.js +1288 -0
- package/dist/durable/seams.d.ts +44 -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/ideas/cli.d.ts +33 -0
- package/dist/ideas/cli.d.ts.map +1 -0
- package/dist/ideas/cli.js +158 -0
- package/dist/ideas/format.d.ts +38 -0
- package/dist/ideas/format.d.ts.map +1 -0
- package/dist/ideas/format.js +194 -0
- package/dist/ideas/index.d.ts +6 -0
- package/dist/ideas/index.d.ts.map +1 -0
- package/dist/ideas/index.js +3 -0
- package/dist/ideas/store.d.ts +55 -0
- package/dist/ideas/store.d.ts.map +1 -0
- package/dist/ideas/store.js +262 -0
- package/dist/ideas/types.d.ts +54 -0
- package/dist/ideas/types.d.ts.map +1 -0
- package/dist/ideas/types.js +26 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -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 +29 -0
- package/dist/init/scaffold.d.ts.map +1 -0
- package/dist/init/scaffold.js +208 -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/managed/index.d.ts +73 -0
- package/dist/managed/index.d.ts.map +1 -0
- package/dist/managed/index.js +222 -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 +186 -0
- package/dist/root/index.d.ts.map +1 -0
- package/dist/root/index.js +430 -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/update/cli.d.ts +8 -0
- package/dist/update/cli.d.ts.map +1 -0
- package/dist/update/cli.js +115 -0
- package/dist/update/index.d.ts +78 -0
- package/dist/update/index.d.ts.map +1 -0
- package/dist/update/index.js +285 -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 +59 -0
- package/templates/default-instance/instruction/.windsurfrules +59 -0
- package/templates/default-instance/instruction/CLAUDE.md +57 -0
- package/templates/default-instance/managed/agent.md +20 -0
- package/templates/default-instance/managed/bootstrap.md +7 -0
- package/templates/default-instance/managed/index-idea.md +5 -0
- package/templates/default-instance/managed/routing-idea.md +14 -0
- package/templates/default-instance/monolith/AGENT.md +1 -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 +151 -0
- package/templates/default-instance/monolith/context/maps/semantic-map.md +75 -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 +32 -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/idea-capture/SKILL.md +157 -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 +111 -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,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable State — a domain-neutral, crash-safe execution record for long-running
|
|
3
|
+
* agentic tasks. See docs/adr/ADR-017-durable-execution-state.md.
|
|
4
|
+
*/
|
|
5
|
+
export { DurableTasks, newOwnerId, parseToken } from './runtime.js';
|
|
6
|
+
export type { DurableTasksOptions, Fence, CreateTaskInput, TaskSummary, DeliveryResult, EffectOptions, EffectOutcome, ResumePacket, VerifyReport, RetentionReport, } from './runtime.js';
|
|
7
|
+
export { FsEventStore, AppendOutcomeUnknownError, MAX_SEGMENT_BYTES } from './fs-store.js';
|
|
8
|
+
export type { FsStoreOptions, FsStoreTarget } from './fs-store.js';
|
|
9
|
+
export { MemoryEventStore } from './memory-store.js';
|
|
10
|
+
export { assessTask, diffStates, leaseIsLive } from './assess.js';
|
|
11
|
+
export type { Assessment, AssessOptions, ResumeMode, Reason, ReasonCode, AssumptionChange, ResultToConsume, StateChange } from './assess.js';
|
|
12
|
+
export { planRetention } from './retention.js';
|
|
13
|
+
export type { RetentionPolicy, RetentionDecision, RetentionAction, InterruptedWork } from './retention.js';
|
|
14
|
+
export { foldEvents, foldUntil, applyEvent } from './projection.js';
|
|
15
|
+
export { MAX_EVENT_BYTES } from './log.js';
|
|
16
|
+
export { toJson, canonicalize, fingerprintOf } from './canonical.js';
|
|
17
|
+
export type { Json } from './canonical.js';
|
|
18
|
+
export * from './errors.js';
|
|
19
|
+
export type { EventStore, TaskEvent, EventType, TaskState, TaskStatus, Tombstone, LogRead, Evidence, Criticality, PlanStepState, AwaitState, EffectState, AssumptionState, StoreDiagnostic, AppendOutcome, } from './types.js';
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/durable/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACpE,YAAY,EACV,mBAAmB,EACnB,KAAK,EACL,eAAe,EACf,WAAW,EACX,cAAc,EACd,aAAa,EACb,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC3F,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC7I,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC3G,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACrE,YAAY,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,cAAc,aAAa,CAAC;AAC5B,YAAY,EACV,UAAU,EACV,SAAS,EACT,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,OAAO,EACP,QAAQ,EACR,WAAW,EACX,aAAa,EACb,UAAU,EACV,WAAW,EACX,eAAe,EACf,eAAe,EACf,aAAa,GACd,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable State — a domain-neutral, crash-safe execution record for long-running
|
|
3
|
+
* agentic tasks. See docs/adr/ADR-017-durable-execution-state.md.
|
|
4
|
+
*/
|
|
5
|
+
export { DurableTasks, newOwnerId, parseToken } from './runtime.js';
|
|
6
|
+
export { FsEventStore, AppendOutcomeUnknownError, MAX_SEGMENT_BYTES } from './fs-store.js';
|
|
7
|
+
export { MemoryEventStore } from './memory-store.js';
|
|
8
|
+
export { assessTask, diffStates, leaseIsLive } from './assess.js';
|
|
9
|
+
export { planRetention } from './retention.js';
|
|
10
|
+
export { foldEvents, foldUntil, applyEvent } from './projection.js';
|
|
11
|
+
export { MAX_EVENT_BYTES } from './log.js';
|
|
12
|
+
export { toJson, canonicalize, fingerprintOf } from './canonical.js';
|
|
13
|
+
export * from './errors.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event envelope: stamping, hashing, strict parsing, chain verification.
|
|
3
|
+
*
|
|
4
|
+
* Each event commits to its predecessor (`prev`) and to its own content
|
|
5
|
+
* (`hash`), and the task id is part of the hashed content. A flipped bit, a
|
|
6
|
+
* deleted or reordered event, or an event copied from another task is
|
|
7
|
+
* therefore detected on read instead of being folded into the state.
|
|
8
|
+
*/
|
|
9
|
+
import { GENESIS_HASH, type EventDraft, type TaskEvent } from './types.js';
|
|
10
|
+
/** Protective ceiling for one event (payloads are application data). */
|
|
11
|
+
export declare const MAX_EVENT_BYTES: number;
|
|
12
|
+
export declare const ID_PATTERN: RegExp;
|
|
13
|
+
export declare function assertId(label: string, value: unknown): asserts value is string;
|
|
14
|
+
export declare function assertNonEmpty(label: string, value: unknown, max: number): asserts value is string;
|
|
15
|
+
type Hashable = Omit<TaskEvent, 'hash'>;
|
|
16
|
+
export declare function computeHash(e: Hashable): string;
|
|
17
|
+
export declare function stampEvent(taskId: string, seq: number, at: string, prev: string, draft: EventDraft): TaskEvent;
|
|
18
|
+
/** One canonical line; byte-identical wherever it is stored (loose file or segment line). */
|
|
19
|
+
export declare function serializeEvent(e: TaskEvent): string;
|
|
20
|
+
/**
|
|
21
|
+
* Parses and verifies one serialized event: envelope shape, known format and
|
|
22
|
+
* type (an unknown one is `LogUnsupportedError`, never skipped), payload shape
|
|
23
|
+
* and the event's own hash. `expectedTaskId` binds it to the task being read.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseEvent(raw: string, expectedTaskId: string, hintSeq: number | null): TaskEvent;
|
|
26
|
+
/**
|
|
27
|
+
* Verifies that `events` continue a chain whose last event had `expectedPrev`
|
|
28
|
+
* and seq `lastSeq`: contiguous seq, `prev` links, nothing after the first
|
|
29
|
+
* violation is trusted. Throws `LogCorruptError`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function verifyChain(taskId: string, events: readonly TaskEvent[], lastSeq: number, expectedPrev: string): void;
|
|
32
|
+
export { GENESIS_HASH };
|
|
33
|
+
//# sourceMappingURL=log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/durable/log.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAGL,YAAY,EACZ,KAAK,UAAU,EAEf,KAAK,SAAS,EACf,MAAM,YAAY,CAAC;AAEpB,wEAAwE;AACxE,eAAO,MAAM,eAAe,QAAa,CAAC;AAE1C,eAAO,MAAM,UAAU,QAAqC,CAAC;AAE7D,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAI/E;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAGlG;AAED,KAAK,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAExC,wBAAgB,WAAW,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAE/C;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,SAAS,CAc9G;AAED,6FAA6F;AAC7F,wBAAgB,cAAc,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAMnD;AAoDD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAiCjG;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,SAAS,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CASrH;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event envelope: stamping, hashing, strict parsing, chain verification.
|
|
3
|
+
*
|
|
4
|
+
* Each event commits to its predecessor (`prev`) and to its own content
|
|
5
|
+
* (`hash`), and the task id is part of the hashed content. A flipped bit, a
|
|
6
|
+
* deleted or reordered event, or an event copied from another task is
|
|
7
|
+
* therefore detected on read instead of being folded into the state.
|
|
8
|
+
*/
|
|
9
|
+
import { canonicalize, sha256Hex, toJson } from './canonical.js';
|
|
10
|
+
import { LogCorruptError, LogUnsupportedError, ValidationError } from './errors.js';
|
|
11
|
+
import { EVENT_FORMAT_VERSION, EVENT_TYPES, GENESIS_HASH, } from './types.js';
|
|
12
|
+
/** Protective ceiling for one event (payloads are application data). */
|
|
13
|
+
export const MAX_EVENT_BYTES = 256 * 1024;
|
|
14
|
+
export const ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
15
|
+
export function assertId(label, value) {
|
|
16
|
+
if (typeof value !== 'string' || !ID_PATTERN.test(value)) {
|
|
17
|
+
throw new ValidationError(`${label} must match ${ID_PATTERN} (letters, digits, "_" and "-", at most 64).`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function assertNonEmpty(label, value, max) {
|
|
21
|
+
if (typeof value !== 'string' || value.trim().length === 0)
|
|
22
|
+
throw new ValidationError(`${label} must be a non-empty string.`);
|
|
23
|
+
if (value.length > max)
|
|
24
|
+
throw new ValidationError(`${label} is longer than ${max} characters.`);
|
|
25
|
+
}
|
|
26
|
+
export function computeHash(e) {
|
|
27
|
+
return sha256Hex(canonicalize(e));
|
|
28
|
+
}
|
|
29
|
+
export function stampEvent(taskId, seq, at, prev, draft) {
|
|
30
|
+
const body = {
|
|
31
|
+
v: EVENT_FORMAT_VERSION,
|
|
32
|
+
taskId,
|
|
33
|
+
seq,
|
|
34
|
+
at,
|
|
35
|
+
type: draft.type,
|
|
36
|
+
actor: draft.actor,
|
|
37
|
+
data: toJson(draft.data, `${draft.type}.data`),
|
|
38
|
+
prev,
|
|
39
|
+
};
|
|
40
|
+
const event = { ...body, hash: computeHash(body) };
|
|
41
|
+
serializeEvent(event); // enforces MAX_EVENT_BYTES before anything touches storage
|
|
42
|
+
return event;
|
|
43
|
+
}
|
|
44
|
+
/** One canonical line; byte-identical wherever it is stored (loose file or segment line). */
|
|
45
|
+
export function serializeEvent(e) {
|
|
46
|
+
const line = canonicalize(e);
|
|
47
|
+
if (Buffer.byteLength(line, 'utf8') > MAX_EVENT_BYTES) {
|
|
48
|
+
throw new ValidationError(`event ${e.type} is larger than ${MAX_EVENT_BYTES} bytes; store large payloads outside the log and reference them.`);
|
|
49
|
+
}
|
|
50
|
+
return line;
|
|
51
|
+
}
|
|
52
|
+
const isStr = (v) => typeof v === 'string';
|
|
53
|
+
const isStrOrNull = (v) => v === null || typeof v === 'string';
|
|
54
|
+
const isInt = (v) => typeof v === 'number' && Number.isInteger(v) && v >= 0;
|
|
55
|
+
const isBool = (v) => typeof v === 'boolean';
|
|
56
|
+
const isAny = (v) => v !== undefined;
|
|
57
|
+
const isStrArr = (v) => Array.isArray(v) && v.every(isStr);
|
|
58
|
+
const isObj = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
59
|
+
const isLabels = (v) => isObj(v) && Object.values(v).every(isStr);
|
|
60
|
+
const isEvidence = (v) => Array.isArray(v) && v.every((e) => isObj(e) && isStr(e.ref) && isStrOrNull(e.note));
|
|
61
|
+
const isSteps = (v) => Array.isArray(v) && v.every((s) => isObj(s) && isStr(s.id) && isStr(s.title) && isStrArr(s.dependsOn));
|
|
62
|
+
const isCrit = (v) => v === 'blocking' || v === 'advisory';
|
|
63
|
+
const isIntOrNull = (v) => v === null || isInt(v);
|
|
64
|
+
const SCHEMA = {
|
|
65
|
+
'task.created': { kind: isStr, goal: isStr, input: isAny, parentTaskId: isStrOrNull, dependsOn: isStrArr, labels: isLabels },
|
|
66
|
+
'run.claimed': { owner: isStr, epoch: isInt, leaseUntil: isStr, takeover: isBool },
|
|
67
|
+
'run.heartbeat': { owner: isStr, epoch: isInt, leaseUntil: isStr },
|
|
68
|
+
'run.released': { owner: isStr, epoch: isInt },
|
|
69
|
+
'plan.declared': { steps: isSteps, reason: isStrOrNull },
|
|
70
|
+
'plan.step_done': { stepId: isStr, result: isAny },
|
|
71
|
+
'plan.step_skipped': { stepId: isStr, reason: isStr },
|
|
72
|
+
'decision.recorded': { id: isStr, summary: isStr, rationale: isStrOrNull, basedOn: isStrArr },
|
|
73
|
+
'assumption.recorded': { key: isStr, statement: isStr, criticality: isCrit, fingerprint: isStr, value: isAny, evidence: isEvidence },
|
|
74
|
+
'assumption.retracted': { key: isStr, reason: isStr },
|
|
75
|
+
'fact.observed': { key: isStr, fingerprint: isStr, value: isAny, source: isStrOrNull },
|
|
76
|
+
'effect.intended': { key: isStr, description: isStr, params: isAny, epoch: isIntOrNull },
|
|
77
|
+
'effect.completed': { key: isStr, result: isAny },
|
|
78
|
+
'effect.failed': { key: isStr, error: isStr, retryable: isBool },
|
|
79
|
+
'await.registered': {
|
|
80
|
+
awaitId: isStr, token: isStr, purpose: isStr, stepId: isStrOrNull, request: isAny, deadlineAt: isStrOrNull, basisSeq: isInt,
|
|
81
|
+
},
|
|
82
|
+
'await.resolved': { awaitId: isStr, idempotencyKey: isStr, payload: isAny, source: isStrOrNull, late: isBool },
|
|
83
|
+
'await.delivery_rejected': { awaitId: isStr, idempotencyKey: isStr, reason: (v) => v === 'already-resolved', payload: isAny },
|
|
84
|
+
'await.expired': { awaitId: isStr },
|
|
85
|
+
'task.completed': { result: isAny },
|
|
86
|
+
'task.failed': { error: isStr, detail: isAny },
|
|
87
|
+
'task.cancelled': { reason: isStr },
|
|
88
|
+
};
|
|
89
|
+
const OPTIONAL_FIELDS = {
|
|
90
|
+
'effect.failed': { uncertain: isBool },
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Parses and verifies one serialized event: envelope shape, known format and
|
|
94
|
+
* type (an unknown one is `LogUnsupportedError`, never skipped), payload shape
|
|
95
|
+
* and the event's own hash. `expectedTaskId` binds it to the task being read.
|
|
96
|
+
*/
|
|
97
|
+
export function parseEvent(raw, expectedTaskId, hintSeq) {
|
|
98
|
+
let parsed;
|
|
99
|
+
try {
|
|
100
|
+
parsed = JSON.parse(raw);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
throw new LogCorruptError(expectedTaskId, hintSeq, 'event is not valid JSON');
|
|
104
|
+
}
|
|
105
|
+
if (!isObj(parsed))
|
|
106
|
+
throw new LogCorruptError(expectedTaskId, hintSeq, 'event is not an object');
|
|
107
|
+
const seq = parsed.seq;
|
|
108
|
+
if (!isInt(seq) || seq < 1)
|
|
109
|
+
throw new LogCorruptError(expectedTaskId, hintSeq, 'event has no valid seq');
|
|
110
|
+
const s = seq;
|
|
111
|
+
if (parsed.v !== EVENT_FORMAT_VERSION)
|
|
112
|
+
throw new LogUnsupportedError(expectedTaskId, s, `event format version`);
|
|
113
|
+
const type = parsed.type;
|
|
114
|
+
if (typeof type !== 'string' || !EVENT_TYPES.includes(type)) {
|
|
115
|
+
throw new LogUnsupportedError(expectedTaskId, s, 'event type');
|
|
116
|
+
}
|
|
117
|
+
if (parsed.taskId !== expectedTaskId)
|
|
118
|
+
throw new LogCorruptError(expectedTaskId, s, 'event belongs to another task');
|
|
119
|
+
if (!isStr(parsed.at) || !isStrOrNull(parsed.actor) || !isStr(parsed.prev) || !isStr(parsed.hash)) {
|
|
120
|
+
throw new LogCorruptError(expectedTaskId, s, 'malformed envelope fields');
|
|
121
|
+
}
|
|
122
|
+
const data = parsed.data;
|
|
123
|
+
const schema = SCHEMA[type];
|
|
124
|
+
if (!isObj(data))
|
|
125
|
+
throw new LogCorruptError(expectedTaskId, s, 'event data is not an object');
|
|
126
|
+
for (const [field, check] of Object.entries(schema)) {
|
|
127
|
+
if (!(field in data) || !check(data[field]))
|
|
128
|
+
throw new LogCorruptError(expectedTaskId, s, `payload field "${field}" of ${type} is invalid`);
|
|
129
|
+
}
|
|
130
|
+
// Fields added after format v1 was fixed: may be absent (older logs), must be well-formed when present.
|
|
131
|
+
for (const [field, check] of Object.entries(OPTIONAL_FIELDS[type] ?? {})) {
|
|
132
|
+
if (field in data && !check(data[field]))
|
|
133
|
+
throw new LogCorruptError(expectedTaskId, s, `payload field "${field}" of ${type} is invalid`);
|
|
134
|
+
}
|
|
135
|
+
const { hash, ...rest } = parsed;
|
|
136
|
+
if (computeHash(rest) !== hash)
|
|
137
|
+
throw new LogCorruptError(expectedTaskId, s, 'content hash mismatch');
|
|
138
|
+
return parsed;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Verifies that `events` continue a chain whose last event had `expectedPrev`
|
|
142
|
+
* and seq `lastSeq`: contiguous seq, `prev` links, nothing after the first
|
|
143
|
+
* violation is trusted. Throws `LogCorruptError`.
|
|
144
|
+
*/
|
|
145
|
+
export function verifyChain(taskId, events, lastSeq, expectedPrev) {
|
|
146
|
+
let seq = lastSeq;
|
|
147
|
+
let prev = expectedPrev;
|
|
148
|
+
for (const e of events) {
|
|
149
|
+
if (e.seq !== seq + 1)
|
|
150
|
+
throw new LogCorruptError(taskId, e.seq, `sequence gap (expected ${seq + 1})`);
|
|
151
|
+
if (e.prev !== prev)
|
|
152
|
+
throw new LogCorruptError(taskId, e.seq, 'prev hash does not match the preceding event');
|
|
153
|
+
seq = e.seq;
|
|
154
|
+
prev = e.hash;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
export { GENESIS_HASH };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory `EventStore`. For tests of application logic built on Durable
|
|
3
|
+
* State and for embedding where durability is not wanted. It implements the
|
|
4
|
+
* same contract as the filesystem store (the shared conformance suite runs
|
|
5
|
+
* against both), including the prune decision register (ADR-019) and the
|
|
6
|
+
* canonical-input rules of compaction (Design A, section 40), but nothing
|
|
7
|
+
* survives the process. There is no version marker: it belongs to the
|
|
8
|
+
* filesystem layout.
|
|
9
|
+
*/
|
|
10
|
+
import type { AppendOutcome, CompactionResult, DecisionBy, EventStore, LogRead, PruneOutcome, PruneState, TaskEvent, Tombstone } from './types.js';
|
|
11
|
+
export declare class MemoryEventStore implements EventStore {
|
|
12
|
+
private readonly tasks;
|
|
13
|
+
private readonly maxAttempts;
|
|
14
|
+
constructor();
|
|
15
|
+
private derive;
|
|
16
|
+
private refusal;
|
|
17
|
+
pruneState(taskId: string): Promise<PruneState>;
|
|
18
|
+
/** F-1: the log is that of a finished task whose head is the one the tombstone names. No `await` between check and write. */
|
|
19
|
+
private assertPrunable;
|
|
20
|
+
preparePrune(tombstone: Tombstone): Promise<{
|
|
21
|
+
attempt: number;
|
|
22
|
+
created: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
decidePrune(taskId: string, attempt: number, outcome: PruneOutcome, tombstone?: Tombstone, by?: DecisionBy): Promise<PruneOutcome>;
|
|
25
|
+
markPruned(tombstone: Tombstone): Promise<void>;
|
|
26
|
+
withdrawTombstone(tombstone: Tombstone): Promise<void>;
|
|
27
|
+
prune(tombstone: Tombstone, opts?: {
|
|
28
|
+
requireCommit?: boolean;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
removedFiles: number;
|
|
31
|
+
}>;
|
|
32
|
+
/** G2 (section 24.2): the register is asked AFTER a write became visible; nothing visible is ever taken back. */
|
|
33
|
+
private settle;
|
|
34
|
+
readLog(taskId: string, opts?: {
|
|
35
|
+
afterSeq?: number;
|
|
36
|
+
rawEvents?: boolean;
|
|
37
|
+
}): Promise<LogRead>;
|
|
38
|
+
append(event: TaskEvent): Promise<AppendOutcome>;
|
|
39
|
+
listTaskIds(): Promise<string[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Nothing to pack in memory. The input is still checked against the canonical log exactly as the filesystem store checks
|
|
42
|
+
* it (CC-1, CC-2): every event must equal the stored one byte for byte, and a slot that has no stored event is refused.
|
|
43
|
+
*/
|
|
44
|
+
compact(taskId: string, throughSeq: number, events: TaskEvent[]): Promise<CompactionResult>;
|
|
45
|
+
pinReference(parentId: string, childId: string, at: string): Promise<'pinned' | 'parent-pruned' | 'parent-missing'>;
|
|
46
|
+
listReferences(parentId: string): Promise<Array<{
|
|
47
|
+
childId: string;
|
|
48
|
+
at: string;
|
|
49
|
+
}>>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=memory-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../src/durable/memory-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAeH,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,OAAO,EACP,YAAY,EACZ,UAAU,EAEV,SAAS,EACT,SAAS,EACV,MAAM,YAAY,CAAC;AAoBpB,qBAAa,gBAAiB,YAAW,UAAU;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4B;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;;IAQrC,OAAO,CAAC,MAAM;IAoBd,OAAO,CAAC,OAAO;IAeT,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAIrD,6HAA6H;IAC7H,OAAO,CAAC,cAAc;IAKhB,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAclF,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC;IAelI,UAAU,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/C,iBAAiB,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAWtD,KAAK,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,GAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAiC5G,iHAAiH;YACnG,MAAM;IAgBd,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBhG,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC;IA4BhD,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAItC;;;OAGG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwB3F,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,eAAe,GAAG,gBAAgB,CAAC;IAanH,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAIxF"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory `EventStore`. For tests of application logic built on Durable
|
|
3
|
+
* State and for embedding where durability is not wanted. It implements the
|
|
4
|
+
* same contract as the filesystem store (the shared conformance suite runs
|
|
5
|
+
* against both), including the prune decision register (ADR-019) and the
|
|
6
|
+
* canonical-input rules of compaction (Design A, section 40), but nothing
|
|
7
|
+
* survives the process. There is no version marker: it belongs to the
|
|
8
|
+
* filesystem layout.
|
|
9
|
+
*/
|
|
10
|
+
import { LogCorruptError, PruneAttemptLimitError, PruneDecisionCorruptError, RetentionRefusedError, StoreIoError, TaskPrunedError, TombstoneCommittedError, } from './errors.js';
|
|
11
|
+
import { tombstoneProblem } from './prune-check.js';
|
|
12
|
+
import { takeMemorySeams } from './seams.js';
|
|
13
|
+
import { serializeEvent } from './log.js';
|
|
14
|
+
import { MAX_PRUNE_ATTEMPTS } from './register.js';
|
|
15
|
+
const MAX_WRITE_RESOLVE = 3;
|
|
16
|
+
export class MemoryEventStore {
|
|
17
|
+
tasks = new Map();
|
|
18
|
+
maxAttempts;
|
|
19
|
+
constructor() {
|
|
20
|
+
this.maxAttempts = takeMemorySeams().maxPruneAttempts ?? MAX_PRUNE_ATTEMPTS;
|
|
21
|
+
}
|
|
22
|
+
// ----- the register ---------------------------------------------------------------------------------------------
|
|
23
|
+
derive(taskId) {
|
|
24
|
+
const entry = this.tasks.get(taskId);
|
|
25
|
+
if (!entry)
|
|
26
|
+
return { kind: 'missing' };
|
|
27
|
+
const commitAt = entry.attempts.findIndex((a) => a.decision?.outcome === 'commit');
|
|
28
|
+
if (commitAt >= 0) {
|
|
29
|
+
return {
|
|
30
|
+
kind: 'committed',
|
|
31
|
+
attempt: commitAt + 1,
|
|
32
|
+
tombstone: structuredClone(entry.attempts[commitAt].tombstone),
|
|
33
|
+
residue: entry.events.length + entry.refs.size,
|
|
34
|
+
durability: 'NOT_GUARANTEED',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const m = entry.attempts.length;
|
|
38
|
+
if (m === 0)
|
|
39
|
+
return { kind: 'live', attempt: 0 };
|
|
40
|
+
const last = entry.attempts[m - 1];
|
|
41
|
+
if (last.decision)
|
|
42
|
+
return { kind: 'live', attempt: m };
|
|
43
|
+
return { kind: 'preparing', attempt: m, tombstone: structuredClone(last.tombstone) };
|
|
44
|
+
}
|
|
45
|
+
refusal(taskId, st) {
|
|
46
|
+
switch (st.kind) {
|
|
47
|
+
case 'corrupt':
|
|
48
|
+
return new PruneDecisionCorruptError(taskId, 'register', st.file ?? null, st.attempt ?? null);
|
|
49
|
+
case 'legacy':
|
|
50
|
+
return new PruneDecisionCorruptError(taskId, 'legacy-tombstone-unmigrated');
|
|
51
|
+
case 'missing':
|
|
52
|
+
return new PruneDecisionCorruptError(taskId, 'task-dir-missing');
|
|
53
|
+
case 'unreadable':
|
|
54
|
+
return new StoreIoError(`the prune state of task "${taskId}" cannot be read now.`);
|
|
55
|
+
default:
|
|
56
|
+
return new TaskPrunedError(taskId);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async pruneState(taskId) {
|
|
60
|
+
return this.derive(taskId);
|
|
61
|
+
}
|
|
62
|
+
/** F-1: the log is that of a finished task whose head is the one the tombstone names. No `await` between check and write. */
|
|
63
|
+
assertPrunable(entry, tombstone) {
|
|
64
|
+
const problem = tombstoneProblem(tombstone.taskId, entry.events, tombstone);
|
|
65
|
+
if (problem !== null)
|
|
66
|
+
throw new RetentionRefusedError(tombstone.taskId, problem);
|
|
67
|
+
}
|
|
68
|
+
async preparePrune(tombstone) {
|
|
69
|
+
const taskId = tombstone.taskId;
|
|
70
|
+
const st = this.derive(taskId);
|
|
71
|
+
if (st.kind === 'missing')
|
|
72
|
+
throw new LogCorruptError(taskId, null, 'cannot prune an unknown task');
|
|
73
|
+
if (st.kind === 'preparing')
|
|
74
|
+
return { attempt: st.attempt, created: false };
|
|
75
|
+
if (st.kind !== 'live')
|
|
76
|
+
throw this.refusal(taskId, st);
|
|
77
|
+
const entry = this.tasks.get(taskId);
|
|
78
|
+
const n = entry.attempts.length + 1;
|
|
79
|
+
if (n > this.maxAttempts)
|
|
80
|
+
throw new PruneAttemptLimitError(taskId, this.maxAttempts);
|
|
81
|
+
this.assertPrunable(entry, tombstone); // synchronous up to the push: nothing can append in between
|
|
82
|
+
entry.attempts.push({ tombstone: structuredClone(tombstone), decision: null });
|
|
83
|
+
return { attempt: n, created: true };
|
|
84
|
+
}
|
|
85
|
+
async decidePrune(taskId, attempt, outcome, tombstone, by) {
|
|
86
|
+
const entry = this.tasks.get(taskId);
|
|
87
|
+
const a = entry?.attempts[attempt - 1];
|
|
88
|
+
if (!entry || !a)
|
|
89
|
+
throw new LogCorruptError(taskId, null, `there is no prepare for prune attempt ${attempt}; a decision needs one`);
|
|
90
|
+
if (outcome !== 'commit' && outcome !== 'abort')
|
|
91
|
+
throw new LogCorruptError(taskId, null, 'a prune decision is either "commit" or "abort"');
|
|
92
|
+
if (outcome === 'commit' && (!tombstone || JSON.stringify(sortKeys(tombstone)) !== JSON.stringify(sortKeys(a.tombstone)))) {
|
|
93
|
+
throw new LogCorruptError(taskId, null, 'a commit must carry the tombstone of its attempt');
|
|
94
|
+
}
|
|
95
|
+
if (!a.decision && outcome === 'commit')
|
|
96
|
+
this.assertPrunable(entry, a.tombstone);
|
|
97
|
+
if (!a.decision)
|
|
98
|
+
a.decision = outcome === 'abort' ? { outcome, ...(by ? { by } : {}) } : { outcome };
|
|
99
|
+
return a.decision.outcome;
|
|
100
|
+
}
|
|
101
|
+
// ----- adapters of the pre-register operations ------------------------------------------------------------------
|
|
102
|
+
async markPruned(tombstone) {
|
|
103
|
+
const st = this.derive(tombstone.taskId);
|
|
104
|
+
if (st.kind === 'preparing' || st.kind === 'committed') {
|
|
105
|
+
if (st.tombstone.headHash !== tombstone.headHash)
|
|
106
|
+
throw new LogCorruptError(tombstone.taskId, null, 'a different tombstone already exists');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
await this.preparePrune(tombstone);
|
|
110
|
+
}
|
|
111
|
+
async withdrawTombstone(tombstone) {
|
|
112
|
+
const taskId = tombstone.taskId;
|
|
113
|
+
const st = this.derive(taskId);
|
|
114
|
+
if (st.kind === 'live' || st.kind === 'missing')
|
|
115
|
+
return;
|
|
116
|
+
if (st.kind === 'committed')
|
|
117
|
+
throw new TombstoneCommittedError(taskId, 'the prune is committed; the tombstone cannot be withdrawn');
|
|
118
|
+
if (st.kind !== 'preparing')
|
|
119
|
+
throw this.refusal(taskId, st);
|
|
120
|
+
if (st.tombstone.headHash !== tombstone.headHash)
|
|
121
|
+
throw new LogCorruptError(taskId, null, 'a different tombstone exists; refusing to withdraw it');
|
|
122
|
+
const w = await this.decidePrune(taskId, st.attempt, 'abort', undefined, 'creator');
|
|
123
|
+
if (w === 'commit')
|
|
124
|
+
throw new TombstoneCommittedError(taskId, 'the prune is committed; the tombstone cannot be withdrawn');
|
|
125
|
+
}
|
|
126
|
+
async prune(tombstone, opts = {}) {
|
|
127
|
+
const taskId = tombstone.taskId;
|
|
128
|
+
let st = this.derive(taskId);
|
|
129
|
+
if (st.kind === 'missing')
|
|
130
|
+
throw new LogCorruptError(taskId, null, 'cannot prune an unknown task');
|
|
131
|
+
if (st.kind === 'live' || st.kind === 'preparing') {
|
|
132
|
+
if (opts.requireCommit)
|
|
133
|
+
throw new RetentionRefusedError(taskId, 'no prune decision has been committed for it');
|
|
134
|
+
if (st.kind === 'live' && (st.attempt ?? 0) > 0)
|
|
135
|
+
throw new RetentionRefusedError(taskId, 'its last prune attempt was aborted by a task that needs it');
|
|
136
|
+
if (st.kind === 'preparing' && st.tombstone.headHash !== tombstone.headHash)
|
|
137
|
+
throw new LogCorruptError(taskId, null, 'a different tombstone already exists');
|
|
138
|
+
// `st` was read before `preparePrune` re-derives: only the attempt seen here (or the first) may be committed.
|
|
139
|
+
const expected = st.kind === 'preparing' ? st.attempt : 1;
|
|
140
|
+
const opened = await this.preparePrune(tombstone);
|
|
141
|
+
const attempt = opened.attempt;
|
|
142
|
+
if (attempt !== expected) {
|
|
143
|
+
if (opened.created)
|
|
144
|
+
await this.decidePrune(taskId, attempt, 'abort', undefined, 'pruner');
|
|
145
|
+
throw new RetentionRefusedError(taskId, 'its prune attempt was aborted by a task that needs it');
|
|
146
|
+
}
|
|
147
|
+
const entry = this.tasks.get(taskId);
|
|
148
|
+
const tomb = entry.attempts[attempt - 1].tombstone;
|
|
149
|
+
if (tomb.headHash !== tombstone.headHash)
|
|
150
|
+
throw new LogCorruptError(taskId, null, 'a different tombstone already exists');
|
|
151
|
+
const w = await this.decidePrune(taskId, attempt, 'commit', tomb, 'pruner');
|
|
152
|
+
if (w === 'abort')
|
|
153
|
+
throw new RetentionRefusedError(taskId, 'the prune attempt was aborted by a task that needs it');
|
|
154
|
+
st = this.derive(taskId);
|
|
155
|
+
}
|
|
156
|
+
if (st.kind !== 'committed')
|
|
157
|
+
throw this.refusal(taskId, st);
|
|
158
|
+
if (st.tombstone.headHash !== tombstone.headHash)
|
|
159
|
+
throw new LogCorruptError(taskId, null, 'a different tombstone already exists');
|
|
160
|
+
const entry = this.tasks.get(taskId);
|
|
161
|
+
entry.tombstone = structuredClone(st.tombstone);
|
|
162
|
+
const removed = entry.events.length;
|
|
163
|
+
entry.events = [];
|
|
164
|
+
entry.refs.clear();
|
|
165
|
+
return { removedFiles: removed };
|
|
166
|
+
}
|
|
167
|
+
/** G2 (section 24.2): the register is asked AFTER a write became visible; nothing visible is ever taken back. */
|
|
168
|
+
async settle(taskId, by) {
|
|
169
|
+
for (let i = 0; i < MAX_WRITE_RESOLVE; i++) {
|
|
170
|
+
const st = this.derive(taskId);
|
|
171
|
+
if (st.kind === 'live')
|
|
172
|
+
return 'CONFIRMED';
|
|
173
|
+
if (st.kind === 'committed')
|
|
174
|
+
return 'PRUNED';
|
|
175
|
+
if (st.kind === 'preparing') {
|
|
176
|
+
await this.decidePrune(taskId, st.attempt, 'abort', undefined, by);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
return st.kind === 'corrupt' || st.kind === 'legacy' ? st : 'UNKNOWN';
|
|
180
|
+
}
|
|
181
|
+
return 'UNKNOWN';
|
|
182
|
+
}
|
|
183
|
+
// ----- EventStore -----------------------------------------------------------------------------------------------
|
|
184
|
+
async readLog(taskId, opts = {}) {
|
|
185
|
+
const entry = this.tasks.get(taskId);
|
|
186
|
+
if (!entry)
|
|
187
|
+
return { exists: false, tombstone: null, events: [], compactedThrough: 0, diagnostics: [] };
|
|
188
|
+
const st = this.derive(taskId);
|
|
189
|
+
const hidden = st.kind === 'committed' || (st.kind === 'preparing' && !opts.rawEvents);
|
|
190
|
+
if (hidden) {
|
|
191
|
+
const diagnostics = entry.events.length > 0 ? [{ code: 'PRUNE_INCOMPLETE', taskId, name: 'events' }] : [];
|
|
192
|
+
return { exists: true, tombstone: structuredClone(st.tombstone), prune: st, events: [], compactedThrough: 0, diagnostics };
|
|
193
|
+
}
|
|
194
|
+
const after = opts.afterSeq ?? 0;
|
|
195
|
+
return {
|
|
196
|
+
exists: true,
|
|
197
|
+
tombstone: null,
|
|
198
|
+
...(st.kind === 'live' ? {} : { prune: st }),
|
|
199
|
+
events: structuredClone(entry.events.filter((e) => e.seq > after)),
|
|
200
|
+
compactedThrough: 0,
|
|
201
|
+
diagnostics: [],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
async append(event) {
|
|
205
|
+
let entry = this.tasks.get(event.taskId);
|
|
206
|
+
// AV: a task is created by its first event only; a later event never creates one (Design A, section 31.1).
|
|
207
|
+
if (!entry && event.seq > 1)
|
|
208
|
+
throw new PruneDecisionCorruptError(event.taskId, 'task-dir-missing');
|
|
209
|
+
if (entry) {
|
|
210
|
+
const st = this.derive(event.taskId);
|
|
211
|
+
if (st.kind === 'committed')
|
|
212
|
+
throw new TaskPrunedError(event.taskId);
|
|
213
|
+
if (event.seq > entry.events.length + 1)
|
|
214
|
+
throw new LogCorruptError(event.taskId, event.seq, 'predecessor event is missing; refusing to publish past a hole');
|
|
215
|
+
}
|
|
216
|
+
if (!entry) {
|
|
217
|
+
entry = { events: [], attempts: [], tombstone: null, refs: new Map() };
|
|
218
|
+
this.tasks.set(event.taskId, entry);
|
|
219
|
+
}
|
|
220
|
+
let created = false;
|
|
221
|
+
if (event.seq <= entry.events.length) {
|
|
222
|
+
// The slot is taken: the same event again is adopted, a different one is a conflict (canonical identity).
|
|
223
|
+
if (serializeEvent(entry.events[event.seq - 1]) !== serializeEvent(event))
|
|
224
|
+
return 'conflict';
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
entry.events.push(structuredClone(event));
|
|
228
|
+
created = true;
|
|
229
|
+
}
|
|
230
|
+
const g2 = await this.settle(event.taskId, 'append');
|
|
231
|
+
if (g2 === 'CONFIRMED')
|
|
232
|
+
return created ? 'appended' : 'adopted';
|
|
233
|
+
if (g2 === 'PRUNED')
|
|
234
|
+
throw new TaskPrunedError(event.taskId);
|
|
235
|
+
if (g2 === 'UNKNOWN')
|
|
236
|
+
throw new StoreIoError(`the prune state of task "${event.taskId}" could not be settled after the append.`);
|
|
237
|
+
throw this.refusal(event.taskId, g2);
|
|
238
|
+
}
|
|
239
|
+
async listTaskIds() {
|
|
240
|
+
return [...this.tasks.keys()].sort();
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Nothing to pack in memory. The input is still checked against the canonical log exactly as the filesystem store checks
|
|
244
|
+
* it (CC-1, CC-2): every event must equal the stored one byte for byte, and a slot that has no stored event is refused.
|
|
245
|
+
*/
|
|
246
|
+
async compact(taskId, throughSeq, events) {
|
|
247
|
+
const entry = this.tasks.get(taskId);
|
|
248
|
+
// The same order as the filesystem store: K0 (pruned), nothing to pack, the shape of the input, the task must exist
|
|
249
|
+
// (compaction never creates one), then every slot against the canonical event (K2).
|
|
250
|
+
if (entry && this.derive(taskId).kind === 'committed')
|
|
251
|
+
throw new TaskPrunedError(taskId);
|
|
252
|
+
if (events.length === 0)
|
|
253
|
+
return { segment: null, packedEvents: 0, removedFiles: 0 };
|
|
254
|
+
const first = events[0];
|
|
255
|
+
const last = events[events.length - 1];
|
|
256
|
+
if (last.seq !== throughSeq || last.seq - first.seq + 1 !== events.length || events.some((e, i) => e.seq !== first.seq + i)) {
|
|
257
|
+
throw new LogCorruptError(taskId, first.seq, 'compaction input is not a contiguous range ending at throughSeq');
|
|
258
|
+
}
|
|
259
|
+
if (!entry)
|
|
260
|
+
throw new PruneDecisionCorruptError(taskId, 'task-dir-missing');
|
|
261
|
+
for (const e of events) {
|
|
262
|
+
const stored = entry.events[e.seq - 1];
|
|
263
|
+
if (!stored)
|
|
264
|
+
throw new LogCorruptError(taskId, e.seq, 'compaction input slot has no canonical event');
|
|
265
|
+
if (serializeEvent(stored) !== serializeEvent(e))
|
|
266
|
+
throw new LogCorruptError(taskId, e.seq, 'compaction input is not the canonical log');
|
|
267
|
+
}
|
|
268
|
+
const g2 = await this.settle(taskId, 'compact');
|
|
269
|
+
if (g2 === 'PRUNED')
|
|
270
|
+
throw new TaskPrunedError(taskId);
|
|
271
|
+
if (g2 === 'UNKNOWN')
|
|
272
|
+
throw new StoreIoError(`the prune state of task "${taskId}" could not be settled after the compaction.`);
|
|
273
|
+
if (g2 !== 'CONFIRMED')
|
|
274
|
+
throw this.refusal(taskId, g2);
|
|
275
|
+
return { segment: null, packedEvents: 0, removedFiles: 0 };
|
|
276
|
+
}
|
|
277
|
+
async pinReference(parentId, childId, at) {
|
|
278
|
+
const entry = this.tasks.get(parentId);
|
|
279
|
+
if (!entry)
|
|
280
|
+
return 'parent-missing';
|
|
281
|
+
const before = this.derive(parentId);
|
|
282
|
+
if (before.kind === 'committed')
|
|
283
|
+
return 'parent-pruned';
|
|
284
|
+
if (!entry.refs.has(childId))
|
|
285
|
+
entry.refs.set(childId, at);
|
|
286
|
+
const g2 = await this.settle(parentId, 'pin');
|
|
287
|
+
if (g2 === 'CONFIRMED')
|
|
288
|
+
return 'pinned';
|
|
289
|
+
if (g2 === 'PRUNED')
|
|
290
|
+
return 'parent-pruned';
|
|
291
|
+
if (g2 === 'UNKNOWN')
|
|
292
|
+
throw new StoreIoError(`the prune state of task "${parentId}" could not be settled after the pin.`);
|
|
293
|
+
throw this.refusal(parentId, g2);
|
|
294
|
+
}
|
|
295
|
+
async listReferences(parentId) {
|
|
296
|
+
const entry = this.tasks.get(parentId);
|
|
297
|
+
return entry ? [...entry.refs].map(([childId, at]) => ({ childId, at })).sort((a, b) => (a.childId < b.childId ? -1 : 1)) : [];
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function sortKeys(o) {
|
|
301
|
+
return Object.fromEntries(Object.entries(o).sort(([a], [b]) => (a < b ? -1 : 1)));
|
|
302
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection — the pure fold from events to `TaskState`.
|
|
3
|
+
*
|
|
4
|
+
* Deterministic and total over valid logs: the same events always give the
|
|
5
|
+
* same state, with no clock, randomness or I/O. It is also *strict*: an event
|
|
6
|
+
* that violates the task's own rules (a second claim with the wrong epoch, a
|
|
7
|
+
* delivery to an unknown await, an event after a terminal one) is a corrupt
|
|
8
|
+
* log, not something to be skipped. Writers run every new event through
|
|
9
|
+
* `applyEvent` before publishing it, so a log that was written by this code
|
|
10
|
+
* always folds.
|
|
11
|
+
*/
|
|
12
|
+
import { GENESIS_HASH, type PlanStepDecl, type TaskEvent, type TaskState } from './types.js';
|
|
13
|
+
/** Idempotency keys are scoped to the await they were delivered to ('awaitId' never contains ':'). */
|
|
14
|
+
export declare function deliveryKey(awaitId: string, idempotencyKey: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* A string-keyed dictionary that does not inherit from `Object.prototype`.
|
|
17
|
+
*
|
|
18
|
+
* Keys of these dictionaries are application data (assumption keys, effect keys, await ids, labels).
|
|
19
|
+
* On a plain object `d['__proto__'] = x` replaces the prototype instead of storing an entry, and
|
|
20
|
+
* `d['constructor']` / `d['toString']` read an inherited function for a key that was never written.
|
|
21
|
+
* A null-prototype object has neither problem; it serializes exactly like a plain one (JSON, canonical
|
|
22
|
+
* form, `Object.keys/values/entries`), so stored events and the public `Record<string, T>` types are unchanged.
|
|
23
|
+
*/
|
|
24
|
+
export declare function newDict<T>(from?: Readonly<Record<string, T>>): Record<string, T>;
|
|
25
|
+
/** Validates a plan declaration; returns the first problem or `null`. Shared by the runtime (nice errors) and the fold (strictness). */
|
|
26
|
+
export declare function planProblem(steps: readonly PlanStepDecl[]): string | null;
|
|
27
|
+
export declare function applyEvent(prevState: TaskState | null, e: TaskEvent): TaskState;
|
|
28
|
+
/** Folds a verified, contiguous list of events (starting at seq 1, or continuing `from`). */
|
|
29
|
+
export declare function foldEvents(events: readonly TaskEvent[], from?: TaskState | null): TaskState;
|
|
30
|
+
/** The state as it was when the log had `seq` events — "what did the task look like when this was sent". */
|
|
31
|
+
export declare function foldUntil(events: readonly TaskEvent[], seq: number): TaskState;
|
|
32
|
+
/** Deep copy. `structuredClone` alone would turn the null-prototype dictionaries back into plain objects. */
|
|
33
|
+
export declare function cloneState(s: TaskState): TaskState;
|
|
34
|
+
export { GENESIS_HASH };
|
|
35
|
+
//# sourceMappingURL=projection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../../src/durable/projection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAsB,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAEjH,sGAAsG;AACtG,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAOhF;AAMD,wIAAwI;AACxI,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,GAAG,IAAI,CA4BzE;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,EAAE,CAAC,EAAE,SAAS,GAAG,SAAS,CAwN/E;AAED,6FAA6F;AAC7F,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,EAAE,IAAI,GAAE,SAAS,GAAG,IAAW,GAAG,SAAS,CAKjG;AAED,4GAA4G;AAC5G,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAE9E;AAED,6GAA6G;AAC7G,wBAAgB,UAAU,CAAC,CAAC,EAAE,SAAS,GAAG,SAAS,CASlD;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|