@world-engines/tmw 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 +46 -0
- package/dist/activation.d.ts +50 -0
- package/dist/activation.js +113 -0
- package/dist/audio-presentation-projection.d.ts +23 -0
- package/dist/audio-presentation-projection.js +39 -0
- package/dist/author-scenario-source-v3.d.ts +34 -0
- package/dist/author-scenario-source-v3.js +1 -0
- package/dist/authoring-runtime.d.ts +76 -0
- package/dist/authoring-runtime.js +282 -0
- package/dist/builder-diagnostics.d.ts +3 -0
- package/dist/builder-diagnostics.js +8 -0
- package/dist/builder-node.d.ts +178 -0
- package/dist/builder-node.js +212 -0
- package/dist/builder.d.ts +32 -0
- package/dist/builder.js +127 -0
- package/dist/capability-registry.d.ts +32 -0
- package/dist/capability-registry.js +33 -0
- package/dist/compile-target-profile.d.ts +16 -0
- package/dist/compile-target-profile.js +70 -0
- package/dist/compiled-plan.d.ts +44 -0
- package/dist/compiled-plan.js +1 -0
- package/dist/compiler-compatibility-gate.d.ts +96 -0
- package/dist/compiler-compatibility-gate.js +609 -0
- package/dist/compiler-release-authority.d.ts +212 -0
- package/dist/compiler-release-authority.js +676 -0
- package/dist/compiler-stage-cache.d.ts +42 -0
- package/dist/compiler-stage-cache.js +100 -0
- package/dist/compiler-stage-registry.d.ts +48 -0
- package/dist/compiler-stage-registry.js +126 -0
- package/dist/compiler.d.ts +7 -0
- package/dist/compiler.js +68 -0
- package/dist/condition-graph.d.ts +46 -0
- package/dist/condition-graph.js +135 -0
- package/dist/diagnostics.d.ts +20 -0
- package/dist/diagnostics.js +20 -0
- package/dist/durable-intent.d.ts +29 -0
- package/dist/durable-intent.js +60 -0
- package/dist/effect-flow.d.ts +69 -0
- package/dist/effect-flow.js +98 -0
- package/dist/event-fabric.d.ts +81 -0
- package/dist/event-fabric.js +227 -0
- package/dist/event-lifecycle.d.ts +59 -0
- package/dist/event-lifecycle.js +161 -0
- package/dist/evidence.d.ts +37 -0
- package/dist/evidence.js +92 -0
- package/dist/gameplay-state.d.ts +127 -0
- package/dist/gameplay-state.js +214 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +52 -0
- package/dist/isolated-replay.d.ts +81 -0
- package/dist/isolated-replay.js +316 -0
- package/dist/music-candidate-set.d.ts +38 -0
- package/dist/music-candidate-set.js +102 -0
- package/dist/natural-language-chunking.d.ts +10 -0
- package/dist/natural-language-chunking.js +62 -0
- package/dist/natural-language-ingestion.d.ts +14 -0
- package/dist/natural-language-ingestion.js +128 -0
- package/dist/natural-language-structured-decode.d.ts +2 -0
- package/dist/natural-language-structured-decode.js +69 -0
- package/dist/natural-language-world-types.d.ts +142 -0
- package/dist/natural-language-world-types.js +2 -0
- package/dist/neutral-source-compiler.d.ts +135 -0
- package/dist/neutral-source-compiler.js +608 -0
- package/dist/presentation-intent.d.ts +42 -0
- package/dist/presentation-intent.js +137 -0
- package/dist/recall-query-envelope.d.ts +32 -0
- package/dist/recall-query-envelope.js +51 -0
- package/dist/rewrite-compensation.d.ts +28 -0
- package/dist/rewrite-compensation.js +85 -0
- package/dist/runtime-contract.d.ts +56 -0
- package/dist/runtime-contract.js +96 -0
- package/dist/runtime-domain-capabilities.d.ts +12 -0
- package/dist/runtime-domain-capabilities.js +20 -0
- package/dist/runtime-sav.d.ts +117 -0
- package/dist/runtime-sav.js +356 -0
- package/dist/runtime-trace.d.ts +44 -0
- package/dist/runtime-trace.js +119 -0
- package/dist/runtime.d.ts +238 -0
- package/dist/runtime.js +1887 -0
- package/dist/scene-author-source.d.ts +13 -0
- package/dist/scene-author-source.js +25 -0
- package/dist/scene-state.d.ts +275 -0
- package/dist/scene-state.js +334 -0
- package/dist/scheduler.d.ts +55 -0
- package/dist/scheduler.js +235 -0
- package/dist/selector-ast.d.ts +24 -0
- package/dist/selector-ast.js +18 -0
- package/dist/semantic-verdict.d.ts +48 -0
- package/dist/semantic-verdict.js +101 -0
- package/dist/simulator.d.ts +45 -0
- package/dist/simulator.js +58 -0
- package/dist/source-canonical.d.ts +4 -0
- package/dist/source-canonical.js +51 -0
- package/dist/source-crypto.d.ts +20 -0
- package/dist/source-crypto.js +17 -0
- package/dist/source-revision.d.ts +22 -0
- package/dist/source-revision.js +43 -0
- package/dist/spatial-impact-receipt.d.ts +38 -0
- package/dist/spatial-impact-receipt.js +67 -0
- package/dist/temporal-rewrite.d.ts +59 -0
- package/dist/temporal-rewrite.js +214 -0
- package/dist/time-domains.d.ts +37 -0
- package/dist/time-domains.js +142 -0
- package/dist/timeline-revision.d.ts +28 -0
- package/dist/timeline-revision.js +59 -0
- package/dist/trigger-document-v3.d.ts +99 -0
- package/dist/trigger-document-v3.js +334 -0
- package/dist/turn-commit.d.ts +56 -0
- package/dist/turn-commit.js +190 -0
- package/dist/validator.d.ts +7 -0
- package/dist/validator.js +142 -0
- package/dist/value-graph.d.ts +98 -0
- package/dist/value-graph.js +318 -0
- package/dist/view-event-delivery.d.ts +47 -0
- package/dist/view-event-delivery.js +197 -0
- package/dist/view-interaction-manifest-v3.d.ts +60 -0
- package/dist/view-interaction-manifest-v3.js +198 -0
- package/dist/view-payload-schema.d.ts +17 -0
- package/dist/view-payload-schema.js +84 -0
- package/package.json +62 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export { AUTHOR_SCENARIO_SOURCE_V3_SCHEMA_VERSION, canonicalRuntimeContractV3Bytes, canonicalTmwBytes, canonicalTmwJson, isSha256Hex, runtimeContractHashV3, tmwSha256Hex, TMW_COMPILED_PLAN_VERSION, TMW_RUNTIME_CONTRACT_V3, TMW_RUNTIME_GENERATION, TRIGGER_DOCUMENT_V3_VERSION, } from "./runtime-contract.js";
|
|
2
|
+
export type { TmwJsonPrimitive, TmwJsonValue, TmwRuntimeRejectionCode } from "./runtime-contract.js";
|
|
3
|
+
export * from "./audio-presentation-projection.js";
|
|
4
|
+
export { orderTmwDiagnostics, tmwDiagnostic, tmwFailure, tmwSuccess } from "./diagnostics.js";
|
|
5
|
+
export type { TmwDiagnostic, TmwDiagnosticSeverity, TmwResult } from "./diagnostics.js";
|
|
6
|
+
export { compareTmwEffectiveTime, isTmwEffectiveTime, TMW_TIME_DOMAINS } from "./time-domains.js";
|
|
7
|
+
export type { ConditionEffectiveTime, EffectEffectiveTime, EventEffectiveTime, PresentationEffectiveTime, TmwEffectiveTime, TmwTimeDomain, } from "./time-domains.js";
|
|
8
|
+
export { SELECTOR_AST_V3_OPS, selectorAstStableIdentity } from "./selector-ast.js";
|
|
9
|
+
export type { SelectorAstV3, SelectorComparisonOperator } from "./selector-ast.js";
|
|
10
|
+
export { capabilityRegistryIndex, getCanonicalTmwCapabilityRegistryV3, isCanonicalTmwCapabilityRegistryV3, TMW_CAPABILITY_REGISTRY_VERSION, } from "./capability-registry.js";
|
|
11
|
+
export type { CapabilityCheckedEffectV3, CanonicalTmwCapabilityRegistryV3, TmwCapabilityPolicy, TmwCapabilityRegistryV3, TmwCapabilityV3, } from "./capability-registry.js";
|
|
12
|
+
export { allTriggerNodesV3, parseTriggerDocumentV3 } from "./trigger-document-v3.js";
|
|
13
|
+
export type { TmwConditionExpressionV3, TriggerConditionNodeV3, TriggerDocumentV3, TriggerEdgeV3, TriggerEffectNodeV3, TriggerEventNodeV3, TriggerNodeKindV3, TriggerNodeV3, TriggerPresentationNodeV3, } from "./trigger-document-v3.js";
|
|
14
|
+
export { tmwBuilderCommandFromNode, tmwBuilderCommandSchema, tmwBuilderNodeFromCommand, tmwStableIdSchema, } from "./builder-node.js";
|
|
15
|
+
export type { TmwBuilderCommand, TmwBuilderNodeCommand, TmwBuilderNodeInput, } from "./builder-node.js";
|
|
16
|
+
export { validateTmw3 } from "./validator.js";
|
|
17
|
+
export type { ValidateTmw3Options } from "./validator.js";
|
|
18
|
+
export { compileTmw3 } from "./compiler.js";
|
|
19
|
+
export { getTmwRuntimeDomainCapability, tmwRuntimeProducesClockDomain } from "./runtime-domain-capabilities.js";
|
|
20
|
+
export type { TmwRuntimeCapabilityTarget, TmwRuntimeDomainCapability } from "./runtime-domain-capabilities.js";
|
|
21
|
+
export type { CompileTmw3Result, CompiledPlanNodeV3, CompiledPlanV3, CompiledPlanV3Core } from "./compiled-plan.js";
|
|
22
|
+
export { simulateTmw3 } from "./simulator.js";
|
|
23
|
+
export type { SimulateTmw3Options, SimulateTmw3Outcome, TmwSimulationResult, TmwSimulationTraceEntry } from "./simulator.js";
|
|
24
|
+
export type { AuthenticatedCreateNewSourceInput, AuthorScenarioSourceV3, AuthorSourceIdentityV3 } from "./author-scenario-source-v3.js";
|
|
25
|
+
export { authorScenarioSourceV3Digest, canonicalAuthorScenarioSourceV3Bytes, createCanonicalEmptyAuthorScenarioSourceV3 } from "./source-canonical.js";
|
|
26
|
+
export { decryptAuthorSourcePayloadV3, encryptAuthorSourcePayloadV3 } from "./source-crypto.js";
|
|
27
|
+
export type { AuthorSourceCryptoPort, EncryptedAuthorSourcePayloadV3 } from "./source-crypto.js";
|
|
28
|
+
export { buildEncryptedAuthorSourceRevisionV3, readbackAuthorSourceRevisionV3 } from "./source-revision.js";
|
|
29
|
+
export type { AuthorSourceReadbackReceiptV3, EncryptedAuthorSourceRevisionV3 } from "./source-revision.js";
|
|
30
|
+
export * from "./activation.js";
|
|
31
|
+
export * from "./condition-graph.js";
|
|
32
|
+
export * from "./durable-intent.js";
|
|
33
|
+
export * from "./effect-flow.js";
|
|
34
|
+
export * from "./event-fabric.js";
|
|
35
|
+
export * from "./event-lifecycle.js";
|
|
36
|
+
export * from "./evidence.js";
|
|
37
|
+
export * from "./isolated-replay.js";
|
|
38
|
+
export * from "./recall-query-envelope.js";
|
|
39
|
+
export * from "./rewrite-compensation.js";
|
|
40
|
+
export * from "./runtime.js";
|
|
41
|
+
export * from "./runtime-sav.js";
|
|
42
|
+
export * from "./runtime-trace.js";
|
|
43
|
+
export * from "./spatial-impact-receipt.js";
|
|
44
|
+
export * from "./scheduler.js";
|
|
45
|
+
export * from "./scene-state.js";
|
|
46
|
+
export * from "./scene-author-source.js";
|
|
47
|
+
export * from "./semantic-verdict.js";
|
|
48
|
+
export * from "./temporal-rewrite.js";
|
|
49
|
+
export * from "./timeline-revision.js";
|
|
50
|
+
export * from "./turn-commit.js";
|
|
51
|
+
export * from "./builder.js";
|
|
52
|
+
export * from "./builder-diagnostics.js";
|
|
53
|
+
export * from "./builder-node.js";
|
|
54
|
+
export * from "./music-candidate-set.js";
|
|
55
|
+
export * from "./presentation-intent.js";
|
|
56
|
+
export * from "./view-interaction-manifest-v3.js";
|
|
57
|
+
export * from "./view-event-delivery.js";
|
|
58
|
+
export * from "./compile-target-profile.js";
|
|
59
|
+
export * from "./compiler-stage-cache.js";
|
|
60
|
+
export * from "./compiler-stage-registry.js";
|
|
61
|
+
export * from "./neutral-source-compiler.js";
|
|
62
|
+
export * from "./compiler-compatibility-gate.js";
|
|
63
|
+
export * from "./compiler-release-authority.js";
|
|
64
|
+
export * from "./scene-author-source.js";
|
|
65
|
+
export * from "./value-graph.js";
|
|
66
|
+
export * from "./gameplay-state.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export { AUTHOR_SCENARIO_SOURCE_V3_SCHEMA_VERSION, canonicalRuntimeContractV3Bytes, canonicalTmwBytes, canonicalTmwJson, isSha256Hex, runtimeContractHashV3, tmwSha256Hex, TMW_COMPILED_PLAN_VERSION, TMW_RUNTIME_CONTRACT_V3, TMW_RUNTIME_GENERATION, TRIGGER_DOCUMENT_V3_VERSION, } from "./runtime-contract.js";
|
|
2
|
+
export * from "./audio-presentation-projection.js";
|
|
3
|
+
export { orderTmwDiagnostics, tmwDiagnostic, tmwFailure, tmwSuccess } from "./diagnostics.js";
|
|
4
|
+
export { compareTmwEffectiveTime, isTmwEffectiveTime, TMW_TIME_DOMAINS } from "./time-domains.js";
|
|
5
|
+
export { SELECTOR_AST_V3_OPS, selectorAstStableIdentity } from "./selector-ast.js";
|
|
6
|
+
export { capabilityRegistryIndex, getCanonicalTmwCapabilityRegistryV3, isCanonicalTmwCapabilityRegistryV3, TMW_CAPABILITY_REGISTRY_VERSION, } from "./capability-registry.js";
|
|
7
|
+
export { allTriggerNodesV3, parseTriggerDocumentV3 } from "./trigger-document-v3.js";
|
|
8
|
+
export { tmwBuilderCommandFromNode, tmwBuilderCommandSchema, tmwBuilderNodeFromCommand, tmwStableIdSchema, } from "./builder-node.js";
|
|
9
|
+
export { validateTmw3 } from "./validator.js";
|
|
10
|
+
export { compileTmw3 } from "./compiler.js";
|
|
11
|
+
export { getTmwRuntimeDomainCapability, tmwRuntimeProducesClockDomain } from "./runtime-domain-capabilities.js";
|
|
12
|
+
export { simulateTmw3 } from "./simulator.js";
|
|
13
|
+
export { authorScenarioSourceV3Digest, canonicalAuthorScenarioSourceV3Bytes, createCanonicalEmptyAuthorScenarioSourceV3 } from "./source-canonical.js";
|
|
14
|
+
export { decryptAuthorSourcePayloadV3, encryptAuthorSourcePayloadV3 } from "./source-crypto.js";
|
|
15
|
+
export { buildEncryptedAuthorSourceRevisionV3, readbackAuthorSourceRevisionV3 } from "./source-revision.js";
|
|
16
|
+
export * from "./activation.js";
|
|
17
|
+
export * from "./condition-graph.js";
|
|
18
|
+
export * from "./durable-intent.js";
|
|
19
|
+
export * from "./effect-flow.js";
|
|
20
|
+
export * from "./event-fabric.js";
|
|
21
|
+
export * from "./event-lifecycle.js";
|
|
22
|
+
export * from "./evidence.js";
|
|
23
|
+
export * from "./isolated-replay.js";
|
|
24
|
+
export * from "./recall-query-envelope.js";
|
|
25
|
+
export * from "./rewrite-compensation.js";
|
|
26
|
+
export * from "./runtime.js";
|
|
27
|
+
export * from "./runtime-sav.js";
|
|
28
|
+
export * from "./runtime-trace.js";
|
|
29
|
+
export * from "./spatial-impact-receipt.js";
|
|
30
|
+
export * from "./scheduler.js";
|
|
31
|
+
export * from "./scene-state.js";
|
|
32
|
+
export * from "./scene-author-source.js";
|
|
33
|
+
export * from "./semantic-verdict.js";
|
|
34
|
+
export * from "./temporal-rewrite.js";
|
|
35
|
+
export * from "./timeline-revision.js";
|
|
36
|
+
export * from "./turn-commit.js";
|
|
37
|
+
export * from "./builder.js";
|
|
38
|
+
export * from "./builder-diagnostics.js";
|
|
39
|
+
export * from "./builder-node.js";
|
|
40
|
+
export * from "./music-candidate-set.js";
|
|
41
|
+
export * from "./presentation-intent.js";
|
|
42
|
+
export * from "./view-interaction-manifest-v3.js";
|
|
43
|
+
export * from "./view-event-delivery.js";
|
|
44
|
+
export * from "./compile-target-profile.js";
|
|
45
|
+
export * from "./compiler-stage-cache.js";
|
|
46
|
+
export * from "./compiler-stage-registry.js";
|
|
47
|
+
export * from "./neutral-source-compiler.js";
|
|
48
|
+
export * from "./compiler-compatibility-gate.js";
|
|
49
|
+
export * from "./compiler-release-authority.js";
|
|
50
|
+
export * from "./scene-author-source.js";
|
|
51
|
+
export * from "./value-graph.js";
|
|
52
|
+
export * from "./gameplay-state.js";
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type TemporalRewriteDraftV3, type TemporalRewriteEventV3 } from "./temporal-rewrite.js";
|
|
2
|
+
import { type RewriteCompensationReceiptV3 } from "./rewrite-compensation.js";
|
|
3
|
+
export interface ReplaySourceEventV3 extends TemporalRewriteEventV3 {
|
|
4
|
+
readonly hardFact: boolean;
|
|
5
|
+
readonly externalIrreversible: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface IsolatedReplayBudget {
|
|
8
|
+
readonly maxEvents: number;
|
|
9
|
+
readonly maxEffects: number;
|
|
10
|
+
readonly maxDepth: number;
|
|
11
|
+
readonly deadlineEpochMs: number;
|
|
12
|
+
}
|
|
13
|
+
export interface TemporalRewriteSemanticVerdictV3 {
|
|
14
|
+
readonly predicateKey: string;
|
|
15
|
+
readonly truth: "true" | "false" | "unknown";
|
|
16
|
+
}
|
|
17
|
+
export interface TemporalRewriteProjectedLedgerV3 {
|
|
18
|
+
readonly schemaVersion: 3;
|
|
19
|
+
readonly sourceEventLedgerDigest: string;
|
|
20
|
+
readonly sourceTimelineRevision: string;
|
|
21
|
+
readonly targetTimelineRevision: string;
|
|
22
|
+
readonly targetWorldTime: number;
|
|
23
|
+
readonly retainedEventIds: ReadonlyArray<string>;
|
|
24
|
+
readonly droppedEventIds: ReadonlyArray<string>;
|
|
25
|
+
readonly affectedEventIds: ReadonlyArray<string>;
|
|
26
|
+
readonly events: ReadonlyArray<TemporalRewriteEventV3>;
|
|
27
|
+
readonly compensationReceipts: ReadonlyArray<RewriteCompensationReceiptV3>;
|
|
28
|
+
readonly compensationReceiptSetDigest: string;
|
|
29
|
+
readonly ledgerDigest: string;
|
|
30
|
+
}
|
|
31
|
+
export interface TemporalRewriteSavCandidateV3 {
|
|
32
|
+
readonly schemaVersion: 3;
|
|
33
|
+
readonly sourceTimelineRevision: string;
|
|
34
|
+
readonly targetTimelineRevision: string;
|
|
35
|
+
readonly targetWorldTime: number;
|
|
36
|
+
readonly projectedLedgerDigest: string;
|
|
37
|
+
readonly compensationReceipts: ReadonlyArray<RewriteCompensationReceiptV3>;
|
|
38
|
+
readonly compensationReceiptDigests: ReadonlyArray<string>;
|
|
39
|
+
readonly candidateDigest: string;
|
|
40
|
+
}
|
|
41
|
+
export interface TemporalRewriteSuccessorInputsV3 {
|
|
42
|
+
readonly revisionId: string;
|
|
43
|
+
readonly parentRevisionId: string;
|
|
44
|
+
readonly revisionSequence: number;
|
|
45
|
+
readonly worldlineId: string;
|
|
46
|
+
readonly operationId: string;
|
|
47
|
+
readonly createdFrom: "temporal_rewrite";
|
|
48
|
+
readonly targetWorldTime: number;
|
|
49
|
+
readonly ledgerHead: string;
|
|
50
|
+
readonly narrativeDigest: string;
|
|
51
|
+
readonly compensationReceiptSetDigest: string;
|
|
52
|
+
readonly savCandidateDigest: string;
|
|
53
|
+
readonly successorInputDigest: string;
|
|
54
|
+
}
|
|
55
|
+
export interface IsolatedReplayResultV3 {
|
|
56
|
+
readonly status: "passed" | "rejected";
|
|
57
|
+
readonly reasonCodes: ReadonlyArray<string>;
|
|
58
|
+
readonly sourceTimelineRevision: string;
|
|
59
|
+
readonly sourceEventLedgerDigest: string;
|
|
60
|
+
readonly projectedEvents: ReadonlyArray<TemporalRewriteEventV3>;
|
|
61
|
+
readonly projectedEventSetDigest: string;
|
|
62
|
+
readonly retainedHardFactIds: ReadonlyArray<string>;
|
|
63
|
+
readonly droppedEventIds: ReadonlyArray<string>;
|
|
64
|
+
readonly affectedEventIds: ReadonlyArray<string>;
|
|
65
|
+
readonly externalEffectsRequiringCompensation: ReadonlyArray<string>;
|
|
66
|
+
readonly projection: TemporalRewriteProjectedLedgerV3 | null;
|
|
67
|
+
readonly savCandidate: TemporalRewriteSavCandidateV3 | null;
|
|
68
|
+
readonly successorInputs: TemporalRewriteSuccessorInputsV3 | null;
|
|
69
|
+
}
|
|
70
|
+
export declare function replayTemporalRewriteIsolated(input: Readonly<{
|
|
71
|
+
draft: TemporalRewriteDraftV3;
|
|
72
|
+
sourceEvents: ReadonlyArray<ReplaySourceEventV3>;
|
|
73
|
+
sourceEventLedgerDigest: string;
|
|
74
|
+
sourceRevisionSequence: number;
|
|
75
|
+
worldlineId: string;
|
|
76
|
+
semanticVerdicts: ReadonlyArray<TemporalRewriteSemanticVerdictV3>;
|
|
77
|
+
knownEvidenceRefs: ReadonlyArray<string>;
|
|
78
|
+
allowedCapabilityIds: ReadonlyArray<string>;
|
|
79
|
+
budget: IsolatedReplayBudget;
|
|
80
|
+
nowEpochMs: number;
|
|
81
|
+
}>): Promise<IsolatedReplayResultV3>;
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { verifyTemporalRewriteDraft, verifyTemporalRewriteEvent, } from "./temporal-rewrite.js";
|
|
2
|
+
import { createRewriteCompensationReceipts, } from "./rewrite-compensation.js";
|
|
3
|
+
import { canonicalTmwJson, canonicalTmwJsonValue, requireTmwIdentifier, requireTmwSafeInteger, requireTmwSha256, tmwSha256Hex, } from "./runtime-trace.js";
|
|
4
|
+
function compareEvents(left, right) {
|
|
5
|
+
return left.effectiveWorldTime - right.effectiveWorldTime
|
|
6
|
+
|| (left.eventId < right.eventId ? -1 : left.eventId > right.eventId ? 1 : 0);
|
|
7
|
+
}
|
|
8
|
+
function exactOrderedIds(actual, expected) {
|
|
9
|
+
return actual.length === expected.length
|
|
10
|
+
&& actual.every((value, index) => value === expected[index]);
|
|
11
|
+
}
|
|
12
|
+
async function canonicalSourceLedger(sourceEvents) {
|
|
13
|
+
const result = [];
|
|
14
|
+
for (const source of sourceEvents) {
|
|
15
|
+
if (typeof source.hardFact !== "boolean" || typeof source.externalIrreversible !== "boolean") {
|
|
16
|
+
throw new Error("tmw_rewrite_source_event_flags_invalid");
|
|
17
|
+
}
|
|
18
|
+
const event = await verifyTemporalRewriteEvent({
|
|
19
|
+
eventId: source.eventId,
|
|
20
|
+
eventType: source.eventType,
|
|
21
|
+
effectiveWorldTime: source.effectiveWorldTime,
|
|
22
|
+
capabilityIds: source.capabilityIds,
|
|
23
|
+
payload: source.payload,
|
|
24
|
+
payloadHash: source.payloadHash,
|
|
25
|
+
replacementOf: source.replacementOf,
|
|
26
|
+
});
|
|
27
|
+
result.push(Object.freeze({
|
|
28
|
+
...event,
|
|
29
|
+
hardFact: source.hardFact,
|
|
30
|
+
externalIrreversible: source.externalIrreversible,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
const eventIds = result.map((event) => event.eventId);
|
|
34
|
+
if (new Set(eventIds).size !== eventIds.length) {
|
|
35
|
+
throw new Error("tmw_rewrite_source_event_id_duplicate");
|
|
36
|
+
}
|
|
37
|
+
const ordered = [...result].sort(compareEvents);
|
|
38
|
+
if (!exactOrderedIds(eventIds, ordered.map((event) => event.eventId))) {
|
|
39
|
+
throw new Error("tmw_rewrite_source_ledger_order_invalid");
|
|
40
|
+
}
|
|
41
|
+
return Object.freeze(result);
|
|
42
|
+
}
|
|
43
|
+
function eventLedgerDigestImage(events) {
|
|
44
|
+
return canonicalTmwJsonValue(events.map((event) => ({
|
|
45
|
+
eventId: event.eventId,
|
|
46
|
+
eventType: event.eventType,
|
|
47
|
+
effectiveWorldTime: event.effectiveWorldTime,
|
|
48
|
+
capabilityIds: event.capabilityIds,
|
|
49
|
+
payload: event.payload,
|
|
50
|
+
payloadHash: event.payloadHash,
|
|
51
|
+
replacementOf: event.replacementOf,
|
|
52
|
+
hardFact: event.hardFact,
|
|
53
|
+
externalIrreversible: event.externalIrreversible,
|
|
54
|
+
})), "rewrite_source_event_ledger");
|
|
55
|
+
}
|
|
56
|
+
function projectedEventDigestImage(events) {
|
|
57
|
+
return canonicalTmwJsonValue(events.map((event) => ({
|
|
58
|
+
eventId: event.eventId,
|
|
59
|
+
eventType: event.eventType,
|
|
60
|
+
effectiveWorldTime: event.effectiveWorldTime,
|
|
61
|
+
capabilityIds: event.capabilityIds,
|
|
62
|
+
payload: event.payload,
|
|
63
|
+
payloadHash: event.payloadHash,
|
|
64
|
+
replacementOf: event.replacementOf,
|
|
65
|
+
})), "rewrite_projected_events");
|
|
66
|
+
}
|
|
67
|
+
function canonicalSemanticVerdicts(input) {
|
|
68
|
+
const verdicts = input.map((verdict) => {
|
|
69
|
+
if (verdict.truth !== "true" && verdict.truth !== "false" && verdict.truth !== "unknown") {
|
|
70
|
+
throw new Error("tmw_rewrite_semantic_truth_invalid");
|
|
71
|
+
}
|
|
72
|
+
return Object.freeze({
|
|
73
|
+
predicateKey: requireTmwIdentifier(verdict.predicateKey, "rewrite_semantic_predicate"),
|
|
74
|
+
truth: verdict.truth,
|
|
75
|
+
});
|
|
76
|
+
}).sort((left, right) => left.predicateKey < right.predicateKey
|
|
77
|
+
? -1
|
|
78
|
+
: left.predicateKey > right.predicateKey
|
|
79
|
+
? 1
|
|
80
|
+
: 0);
|
|
81
|
+
if (new Set(verdicts.map((verdict) => verdict.predicateKey)).size !== verdicts.length) {
|
|
82
|
+
throw new Error("tmw_rewrite_semantic_predicate_duplicate");
|
|
83
|
+
}
|
|
84
|
+
return Object.freeze(verdicts);
|
|
85
|
+
}
|
|
86
|
+
async function rejectedResult(input) {
|
|
87
|
+
return Object.freeze({
|
|
88
|
+
status: "rejected",
|
|
89
|
+
reasonCodes: Object.freeze([...input.reasons].sort()),
|
|
90
|
+
sourceTimelineRevision: input.draft.timelineRevision,
|
|
91
|
+
sourceEventLedgerDigest: input.sourceEventLedgerDigest,
|
|
92
|
+
projectedEvents: Object.freeze([]),
|
|
93
|
+
projectedEventSetDigest: await tmwSha256Hex(canonicalTmwJson([])),
|
|
94
|
+
retainedHardFactIds: Object.freeze([...input.retainedHardFactIds]),
|
|
95
|
+
droppedEventIds: Object.freeze([...input.droppedEventIds]),
|
|
96
|
+
affectedEventIds: Object.freeze([...input.affectedEventIds]),
|
|
97
|
+
externalEffectsRequiringCompensation: Object.freeze([
|
|
98
|
+
...input.externalEffectsRequiringCompensation,
|
|
99
|
+
]),
|
|
100
|
+
projection: null,
|
|
101
|
+
savCandidate: null,
|
|
102
|
+
successorInputs: null,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
export async function replayTemporalRewriteIsolated(input) {
|
|
106
|
+
const draft = await verifyTemporalRewriteDraft(input.draft);
|
|
107
|
+
const sourceEvents = await canonicalSourceLedger(input.sourceEvents);
|
|
108
|
+
const claimedSourceLedgerDigest = requireTmwSha256(input.sourceEventLedgerDigest, "rewrite_source_event_ledger_digest");
|
|
109
|
+
const computedSourceLedgerDigest = await tmwSha256Hex(canonicalTmwJson(eventLedgerDigestImage(sourceEvents)));
|
|
110
|
+
const sourceRevisionSequence = requireTmwSafeInteger(input.sourceRevisionSequence, "rewrite_source_revision_sequence");
|
|
111
|
+
if (sourceRevisionSequence === Number.MAX_SAFE_INTEGER) {
|
|
112
|
+
throw new Error("tmw_rewrite_revision_sequence_exhausted");
|
|
113
|
+
}
|
|
114
|
+
const worldlineId = requireTmwIdentifier(input.worldlineId, "rewrite_worldline_id");
|
|
115
|
+
const semanticVerdicts = canonicalSemanticVerdicts(input.semanticVerdicts);
|
|
116
|
+
const budget = Object.freeze({
|
|
117
|
+
maxEvents: requireTmwSafeInteger(input.budget.maxEvents, "replay_max_events", 1),
|
|
118
|
+
maxEffects: requireTmwSafeInteger(input.budget.maxEffects, "replay_max_effects", 1),
|
|
119
|
+
maxDepth: requireTmwSafeInteger(input.budget.maxDepth, "replay_max_depth", 1),
|
|
120
|
+
deadlineEpochMs: requireTmwSafeInteger(input.budget.deadlineEpochMs, "replay_deadline_epoch_ms", 1),
|
|
121
|
+
});
|
|
122
|
+
const now = requireTmwSafeInteger(input.nowEpochMs, "replay_now_epoch_ms");
|
|
123
|
+
const reasons = new Set();
|
|
124
|
+
if (computedSourceLedgerDigest !== claimedSourceLedgerDigest) {
|
|
125
|
+
reasons.add("rewrite_source_ledger_digest_mismatch");
|
|
126
|
+
}
|
|
127
|
+
if (now >= budget.deadlineEpochMs)
|
|
128
|
+
reasons.add("rewrite_budget_deadline_exhausted");
|
|
129
|
+
if (sourceEvents.length + draft.events.length > budget.maxEvents) {
|
|
130
|
+
reasons.add("rewrite_event_budget_exhausted");
|
|
131
|
+
}
|
|
132
|
+
if (draft.events.some((event) => event.replacementOf.length > budget.maxDepth)) {
|
|
133
|
+
reasons.add("rewrite_depth_budget_exhausted");
|
|
134
|
+
}
|
|
135
|
+
if (sourceEvents.some((event) => event.effectiveWorldTime > draft.interval.toWorldTime)) {
|
|
136
|
+
reasons.add("rewrite_source_event_outside_interval");
|
|
137
|
+
}
|
|
138
|
+
const knownEvidenceRefs = input.knownEvidenceRefs.map((ref) => requireTmwIdentifier(ref, "replay_evidence_ref"));
|
|
139
|
+
if (new Set(knownEvidenceRefs).size !== knownEvidenceRefs.length) {
|
|
140
|
+
throw new Error("tmw_rewrite_known_evidence_ref_duplicate");
|
|
141
|
+
}
|
|
142
|
+
const evidence = new Set(knownEvidenceRefs);
|
|
143
|
+
if (draft.evidenceRefs.some((ref) => !evidence.has(ref))) {
|
|
144
|
+
reasons.add("rewrite_evidence_ref_missing");
|
|
145
|
+
}
|
|
146
|
+
if (semanticVerdicts.some((verdict) => verdict.truth === "unknown")) {
|
|
147
|
+
reasons.add("rewrite_semantic_unknown");
|
|
148
|
+
}
|
|
149
|
+
if (semanticVerdicts.some((verdict) => verdict.truth === "false")) {
|
|
150
|
+
reasons.add("rewrite_semantic_rejected");
|
|
151
|
+
}
|
|
152
|
+
const sourceById = new Map(sourceEvents.map((event) => [event.eventId, event]));
|
|
153
|
+
const draftIds = new Set(draft.events.map((event) => event.eventId));
|
|
154
|
+
if (draft.events.some((event) => sourceById.has(event.eventId))) {
|
|
155
|
+
reasons.add("rewrite_source_replacement_event_id_collision");
|
|
156
|
+
}
|
|
157
|
+
if (draft.replacedEventIds.some((eventId) => !sourceById.has(eventId))) {
|
|
158
|
+
reasons.add("rewrite_replaced_event_missing");
|
|
159
|
+
}
|
|
160
|
+
if (draft.replacedEventIds.some((eventId) => {
|
|
161
|
+
const event = sourceById.get(eventId);
|
|
162
|
+
return event !== undefined
|
|
163
|
+
&& (event.effectiveWorldTime < draft.interval.fromWorldTime
|
|
164
|
+
|| event.effectiveWorldTime > draft.interval.toWorldTime);
|
|
165
|
+
})) {
|
|
166
|
+
reasons.add("rewrite_replaced_event_outside_interval");
|
|
167
|
+
}
|
|
168
|
+
const replacementClaims = new Set(draft.events.flatMap((event) => event.replacementOf));
|
|
169
|
+
if (draft.replacedEventIds.some((eventId) => !replacementClaims.has(eventId))) {
|
|
170
|
+
reasons.add("rewrite_replacement_lineage_missing");
|
|
171
|
+
}
|
|
172
|
+
const replaced = new Set(draft.replacedEventIds);
|
|
173
|
+
const dropped = sourceEvents.filter((event) => event.effectiveWorldTime > draft.interval.targetWorldTime || replaced.has(event.eventId));
|
|
174
|
+
const droppedEventIds = dropped.map((event) => event.eventId).sort();
|
|
175
|
+
const affectedEventIds = Object.freeze([...droppedEventIds]);
|
|
176
|
+
const retained = sourceEvents.filter((event) => !droppedEventIds.includes(event.eventId));
|
|
177
|
+
const retainedHardFactIds = sourceEvents
|
|
178
|
+
.filter((event) => event.hardFact && !droppedEventIds.includes(event.eventId))
|
|
179
|
+
.map((event) => event.eventId)
|
|
180
|
+
.sort();
|
|
181
|
+
const droppedHardFacts = dropped.filter((event) => event.hardFact);
|
|
182
|
+
if (droppedHardFacts.some((event) => !replaced.has(event.eventId))) {
|
|
183
|
+
reasons.add("rewrite_hard_fact_drop_undeclared");
|
|
184
|
+
}
|
|
185
|
+
if (droppedHardFacts.some((event) => !replacementClaims.has(event.eventId))) {
|
|
186
|
+
reasons.add("rewrite_hard_fact_replacement_missing");
|
|
187
|
+
}
|
|
188
|
+
const irreversible = dropped
|
|
189
|
+
.filter((event) => event.externalIrreversible)
|
|
190
|
+
.map((event) => event.eventId)
|
|
191
|
+
.sort();
|
|
192
|
+
const proposalIds = draft.compensationProposals
|
|
193
|
+
.map((proposal) => proposal.externalEffectId)
|
|
194
|
+
.sort();
|
|
195
|
+
if (irreversible.some((eventId) => !proposalIds.includes(eventId))) {
|
|
196
|
+
reasons.add("rewrite_external_compensation_missing");
|
|
197
|
+
}
|
|
198
|
+
if (proposalIds.some((eventId) => !irreversible.includes(eventId))) {
|
|
199
|
+
reasons.add("rewrite_external_compensation_unbound");
|
|
200
|
+
}
|
|
201
|
+
const projectedEvents = [...retained, ...draft.events].sort(compareEvents);
|
|
202
|
+
if (new Set(projectedEvents.map((event) => event.eventId)).size !== projectedEvents.length
|
|
203
|
+
|| draftIds.size !== draft.events.length) {
|
|
204
|
+
reasons.add("rewrite_projected_event_duplicate");
|
|
205
|
+
}
|
|
206
|
+
const allowedCapabilityIds = input.allowedCapabilityIds.map((id) => requireTmwIdentifier(id, "replay_capability_id"));
|
|
207
|
+
if (new Set(allowedCapabilityIds).size !== allowedCapabilityIds.length) {
|
|
208
|
+
throw new Error("tmw_rewrite_allowed_capability_duplicate");
|
|
209
|
+
}
|
|
210
|
+
const allowedCapabilities = new Set(allowedCapabilityIds);
|
|
211
|
+
if (projectedEvents.some((event) => event.capabilityIds.some((capabilityId) => !allowedCapabilities.has(capabilityId)))) {
|
|
212
|
+
reasons.add("rewrite_capability_forbidden");
|
|
213
|
+
}
|
|
214
|
+
const effectCount = projectedEvents.reduce((total, event) => total + event.capabilityIds.length, 0);
|
|
215
|
+
if (effectCount > budget.maxEffects)
|
|
216
|
+
reasons.add("rewrite_effect_budget_exhausted");
|
|
217
|
+
if (reasons.size !== 0) {
|
|
218
|
+
return await rejectedResult({
|
|
219
|
+
draft,
|
|
220
|
+
sourceEventLedgerDigest: computedSourceLedgerDigest,
|
|
221
|
+
reasons,
|
|
222
|
+
retainedHardFactIds,
|
|
223
|
+
droppedEventIds,
|
|
224
|
+
affectedEventIds,
|
|
225
|
+
externalEffectsRequiringCompensation: irreversible,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
const projectedEventImage = projectedEventDigestImage(projectedEvents);
|
|
229
|
+
const projectedEventSetDigest = await tmwSha256Hex(canonicalTmwJson(projectedEventImage));
|
|
230
|
+
const successorSeedDigest = await tmwSha256Hex(canonicalTmwJson({
|
|
231
|
+
epoch: draft.epoch,
|
|
232
|
+
runtimeContractHash: draft.runtimeContractHash,
|
|
233
|
+
artifactDigest: draft.artifactDigest,
|
|
234
|
+
sourceRevision: draft.sourceRevision,
|
|
235
|
+
sourceTimelineRevision: draft.timelineRevision,
|
|
236
|
+
sourceRevisionSequence,
|
|
237
|
+
worldlineId,
|
|
238
|
+
operationId: draft.operationId,
|
|
239
|
+
draftDigest: draft.draftDigest,
|
|
240
|
+
sourceEventLedgerDigest: computedSourceLedgerDigest,
|
|
241
|
+
targetWorldTime: draft.interval.targetWorldTime,
|
|
242
|
+
projectedEventSetDigest,
|
|
243
|
+
compensationProposals: draft.compensationProposals,
|
|
244
|
+
}));
|
|
245
|
+
const targetTimelineRevision = requireTmwIdentifier(`tmw3-rewrite-${successorSeedDigest}`, "rewrite_target_timeline_revision");
|
|
246
|
+
const compensation = await createRewriteCompensationReceipts({
|
|
247
|
+
operationId: draft.operationId,
|
|
248
|
+
sourceTimelineRevision: draft.timelineRevision,
|
|
249
|
+
targetTimelineRevision,
|
|
250
|
+
proposals: draft.compensationProposals,
|
|
251
|
+
});
|
|
252
|
+
const projectionImage = canonicalTmwJsonValue({
|
|
253
|
+
schemaVersion: 3,
|
|
254
|
+
sourceEventLedgerDigest: computedSourceLedgerDigest,
|
|
255
|
+
sourceTimelineRevision: draft.timelineRevision,
|
|
256
|
+
targetTimelineRevision,
|
|
257
|
+
targetWorldTime: draft.interval.targetWorldTime,
|
|
258
|
+
retainedEventIds: retained.map((event) => event.eventId).sort(),
|
|
259
|
+
droppedEventIds,
|
|
260
|
+
affectedEventIds,
|
|
261
|
+
events: projectedEvents,
|
|
262
|
+
compensationReceipts: compensation.receipts,
|
|
263
|
+
compensationReceiptSetDigest: compensation.receiptSetDigest,
|
|
264
|
+
}, "rewrite_projection");
|
|
265
|
+
const ledgerDigest = await tmwSha256Hex(canonicalTmwJson(projectionImage));
|
|
266
|
+
const projection = Object.freeze({
|
|
267
|
+
...projectionImage,
|
|
268
|
+
ledgerDigest,
|
|
269
|
+
});
|
|
270
|
+
const compensationReceiptDigests = Object.freeze(compensation.receipts.map((receipt) => receipt.receiptDigest));
|
|
271
|
+
const savCandidateImage = canonicalTmwJsonValue({
|
|
272
|
+
schemaVersion: 3,
|
|
273
|
+
sourceTimelineRevision: draft.timelineRevision,
|
|
274
|
+
targetTimelineRevision,
|
|
275
|
+
targetWorldTime: draft.interval.targetWorldTime,
|
|
276
|
+
projectedLedgerDigest: ledgerDigest,
|
|
277
|
+
compensationReceipts: compensation.receipts,
|
|
278
|
+
compensationReceiptDigests,
|
|
279
|
+
}, "rewrite_sav_candidate");
|
|
280
|
+
const savCandidate = Object.freeze({
|
|
281
|
+
...savCandidateImage,
|
|
282
|
+
candidateDigest: await tmwSha256Hex(canonicalTmwJson(savCandidateImage)),
|
|
283
|
+
});
|
|
284
|
+
const successorImage = canonicalTmwJsonValue({
|
|
285
|
+
revisionId: targetTimelineRevision,
|
|
286
|
+
parentRevisionId: draft.timelineRevision,
|
|
287
|
+
revisionSequence: sourceRevisionSequence + 1,
|
|
288
|
+
worldlineId,
|
|
289
|
+
operationId: draft.operationId,
|
|
290
|
+
createdFrom: "temporal_rewrite",
|
|
291
|
+
targetWorldTime: draft.interval.targetWorldTime,
|
|
292
|
+
ledgerHead: ledgerDigest,
|
|
293
|
+
narrativeDigest: draft.narrativeDigest,
|
|
294
|
+
compensationReceiptSetDigest: compensation.receiptSetDigest,
|
|
295
|
+
savCandidateDigest: savCandidate.candidateDigest,
|
|
296
|
+
}, "rewrite_successor_inputs");
|
|
297
|
+
const successorInputs = Object.freeze({
|
|
298
|
+
...successorImage,
|
|
299
|
+
successorInputDigest: await tmwSha256Hex(canonicalTmwJson(successorImage)),
|
|
300
|
+
});
|
|
301
|
+
return Object.freeze({
|
|
302
|
+
status: "passed",
|
|
303
|
+
reasonCodes: Object.freeze([]),
|
|
304
|
+
sourceTimelineRevision: draft.timelineRevision,
|
|
305
|
+
sourceEventLedgerDigest: computedSourceLedgerDigest,
|
|
306
|
+
projectedEvents: Object.freeze(projectedEvents),
|
|
307
|
+
projectedEventSetDigest,
|
|
308
|
+
retainedHardFactIds: Object.freeze(retainedHardFactIds),
|
|
309
|
+
droppedEventIds: Object.freeze(droppedEventIds),
|
|
310
|
+
affectedEventIds,
|
|
311
|
+
externalEffectsRequiringCompensation: Object.freeze(irreversible),
|
|
312
|
+
projection,
|
|
313
|
+
savCandidate,
|
|
314
|
+
successorInputs,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { TMW_RUNTIME_GENERATION } from "./runtime-contract.js";
|
|
2
|
+
export type MusicEligibilityPolicyV3 = "normal" | "override_only";
|
|
3
|
+
export interface MusicCandidateV3 {
|
|
4
|
+
readonly resource_id: string;
|
|
5
|
+
readonly policy: MusicEligibilityPolicyV3;
|
|
6
|
+
readonly continuity_key?: string;
|
|
7
|
+
}
|
|
8
|
+
/** ArtifactV5 编译器消费的资源资格 source;运行时 binding/revision 不由作者输入。 */
|
|
9
|
+
export interface MusicCandidateSourceV3 {
|
|
10
|
+
readonly schema: "music-candidate-source.v3";
|
|
11
|
+
readonly generation: typeof TMW_RUNTIME_GENERATION;
|
|
12
|
+
readonly candidates: ReadonlyArray<MusicCandidateV3>;
|
|
13
|
+
}
|
|
14
|
+
export interface MusicCandidateSetV3 {
|
|
15
|
+
readonly schema: "music-candidate-set.v3";
|
|
16
|
+
readonly generation: typeof TMW_RUNTIME_GENERATION;
|
|
17
|
+
readonly candidate_set_id: string;
|
|
18
|
+
readonly revision: number;
|
|
19
|
+
readonly eligibility_revision: number;
|
|
20
|
+
readonly epoch: number;
|
|
21
|
+
readonly artifact_digest: string;
|
|
22
|
+
readonly timeline_revision: string;
|
|
23
|
+
readonly candidates: ReadonlyArray<MusicCandidateV3>;
|
|
24
|
+
}
|
|
25
|
+
export interface MusicCandidateSetBindingV3 {
|
|
26
|
+
readonly candidate_set_id: string;
|
|
27
|
+
readonly revision: number;
|
|
28
|
+
readonly eligibility_revision: number;
|
|
29
|
+
readonly epoch: number;
|
|
30
|
+
readonly artifact_digest: string;
|
|
31
|
+
readonly timeline_revision: string;
|
|
32
|
+
}
|
|
33
|
+
export declare const parseMusicCandidateSourceV3: (value: unknown) => MusicCandidateSourceV3 | null;
|
|
34
|
+
/** Materializes author-owned candidates under a committed host-owned revision. */
|
|
35
|
+
export declare const materializeMusicCandidateSetV3: (sourceInput: unknown, binding: MusicCandidateSetBindingV3) => MusicCandidateSetV3 | null;
|
|
36
|
+
export declare const musicCandidateSetError: (set: MusicCandidateSetV3) => string | null;
|
|
37
|
+
export declare const containsMusicCandidate: (set: MusicCandidateSetV3, resourceId: string, allowOverride: boolean) => boolean;
|
|
38
|
+
export declare const selectMusicCandidate: (set: MusicCandidateSetV3, preferredResourceId: string | null, allowOverride: boolean) => string | null;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { TMW_RUNTIME_GENERATION } from "./runtime-contract.js";
|
|
2
|
+
const SHA256_HEX = /^[a-f0-9]{64}$/;
|
|
3
|
+
const STABLE_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
4
|
+
const MAX_CANDIDATES = 512;
|
|
5
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
+
const exactKeys = (value, keys) => Object.keys(value).sort().join("\0") === [...keys].sort().join("\0");
|
|
7
|
+
const parseCandidate = (value) => {
|
|
8
|
+
if (!isRecord(value))
|
|
9
|
+
return null;
|
|
10
|
+
const keys = value.continuity_key === undefined
|
|
11
|
+
? ["policy", "resource_id"]
|
|
12
|
+
: ["continuity_key", "policy", "resource_id"];
|
|
13
|
+
if (!exactKeys(value, keys))
|
|
14
|
+
return null;
|
|
15
|
+
if (typeof value.resource_id !== "string" || !STABLE_ID.test(value.resource_id))
|
|
16
|
+
return null;
|
|
17
|
+
if (value.policy !== "normal" && value.policy !== "override_only")
|
|
18
|
+
return null;
|
|
19
|
+
if (value.continuity_key !== undefined
|
|
20
|
+
&& (typeof value.continuity_key !== "string" || !STABLE_ID.test(value.continuity_key)))
|
|
21
|
+
return null;
|
|
22
|
+
return value;
|
|
23
|
+
};
|
|
24
|
+
export const parseMusicCandidateSourceV3 = (value) => {
|
|
25
|
+
if (!isRecord(value) || !exactKeys(value, ["candidates", "generation", "schema"]))
|
|
26
|
+
return null;
|
|
27
|
+
if (value.schema !== "music-candidate-source.v3" || value.generation !== 3)
|
|
28
|
+
return null;
|
|
29
|
+
if (!Array.isArray(value.candidates) || value.candidates.length > MAX_CANDIDATES)
|
|
30
|
+
return null;
|
|
31
|
+
const candidates = value.candidates.map(parseCandidate);
|
|
32
|
+
if (!candidates.every((candidate) => candidate !== null))
|
|
33
|
+
return null;
|
|
34
|
+
const ids = new Set(candidates.map((candidate) => candidate.resource_id));
|
|
35
|
+
if (ids.size !== candidates.length)
|
|
36
|
+
return null;
|
|
37
|
+
return value;
|
|
38
|
+
};
|
|
39
|
+
/** Materializes author-owned candidates under a committed host-owned revision. */
|
|
40
|
+
export const materializeMusicCandidateSetV3 = (sourceInput, binding) => {
|
|
41
|
+
const source = parseMusicCandidateSourceV3(sourceInput);
|
|
42
|
+
if (source === null)
|
|
43
|
+
return null;
|
|
44
|
+
const set = Object.freeze({
|
|
45
|
+
schema: "music-candidate-set.v3",
|
|
46
|
+
generation: TMW_RUNTIME_GENERATION,
|
|
47
|
+
candidate_set_id: binding.candidate_set_id,
|
|
48
|
+
revision: binding.revision,
|
|
49
|
+
eligibility_revision: binding.eligibility_revision,
|
|
50
|
+
epoch: binding.epoch,
|
|
51
|
+
artifact_digest: binding.artifact_digest,
|
|
52
|
+
timeline_revision: binding.timeline_revision,
|
|
53
|
+
candidates: Object.freeze(source.candidates.map((candidate) => Object.freeze({ ...candidate })))
|
|
54
|
+
});
|
|
55
|
+
return musicCandidateSetError(set) === null ? set : null;
|
|
56
|
+
};
|
|
57
|
+
export const musicCandidateSetError = (set) => {
|
|
58
|
+
if (set.schema !== "music-candidate-set.v3" || set.generation !== 3)
|
|
59
|
+
return "music_candidate_set_generation_invalid";
|
|
60
|
+
if (!STABLE_ID.test(set.candidate_set_id))
|
|
61
|
+
return "music_candidate_set_id_invalid";
|
|
62
|
+
if (!Number.isSafeInteger(set.revision) || set.revision <= 0)
|
|
63
|
+
return "music_candidate_set_revision_invalid";
|
|
64
|
+
if (!Number.isSafeInteger(set.eligibility_revision) || set.eligibility_revision <= 0) {
|
|
65
|
+
return "music_eligibility_revision_invalid";
|
|
66
|
+
}
|
|
67
|
+
if (!Number.isSafeInteger(set.epoch) || set.epoch <= 0)
|
|
68
|
+
return "content_epoch_required";
|
|
69
|
+
if (!SHA256_HEX.test(set.artifact_digest))
|
|
70
|
+
return "music_candidate_artifact_invalid";
|
|
71
|
+
if (!STABLE_ID.test(set.timeline_revision)) {
|
|
72
|
+
return "music_candidate_timeline_revision_invalid";
|
|
73
|
+
}
|
|
74
|
+
if (set.candidates.length > MAX_CANDIDATES)
|
|
75
|
+
return "music_candidate_set_limit_exceeded";
|
|
76
|
+
const ids = new Set();
|
|
77
|
+
for (const candidate of set.candidates) {
|
|
78
|
+
if (!STABLE_ID.test(candidate.resource_id) || ids.has(candidate.resource_id))
|
|
79
|
+
return "music_candidate_resource_invalid";
|
|
80
|
+
if (candidate.policy !== "normal" && candidate.policy !== "override_only")
|
|
81
|
+
return "music_candidate_policy_invalid";
|
|
82
|
+
if (candidate.continuity_key !== undefined && !STABLE_ID.test(candidate.continuity_key)) {
|
|
83
|
+
return "music_candidate_continuity_key_invalid";
|
|
84
|
+
}
|
|
85
|
+
ids.add(candidate.resource_id);
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
};
|
|
89
|
+
export const containsMusicCandidate = (set, resourceId, allowOverride) => {
|
|
90
|
+
if (musicCandidateSetError(set) !== null)
|
|
91
|
+
return false;
|
|
92
|
+
const candidate = set.candidates.find((item) => item.resource_id === resourceId);
|
|
93
|
+
return candidate !== undefined && (candidate.policy === "normal" || allowOverride);
|
|
94
|
+
};
|
|
95
|
+
export const selectMusicCandidate = (set, preferredResourceId, allowOverride) => {
|
|
96
|
+
if (musicCandidateSetError(set) !== null || set.candidates.length === 0)
|
|
97
|
+
return null;
|
|
98
|
+
if (preferredResourceId !== null) {
|
|
99
|
+
return containsMusicCandidate(set, preferredResourceId, allowOverride) ? preferredResourceId : null;
|
|
100
|
+
}
|
|
101
|
+
return set.candidates.find((candidate) => candidate.policy === "normal" || allowOverride)?.resource_id ?? null;
|
|
102
|
+
};
|