@forgeax/engine-ddc 0.0.0-dev.8d955ade1c79
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 +202 -0
- package/README.md +147 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/concurrency.integration.test.d.ts +2 -0
- package/dist/__tests__/concurrency.integration.test.d.ts.map +1 -0
- package/dist/__tests__/consumer-path.integration.test.d.ts +2 -0
- package/dist/__tests__/consumer-path.integration.test.d.ts.map +1 -0
- package/dist/__tests__/crash-recovery.integration.test.d.ts +2 -0
- package/dist/__tests__/crash-recovery.integration.test.d.ts.map +1 -0
- package/dist/__tests__/entry-store.integration.test.d.ts +2 -0
- package/dist/__tests__/entry-store.integration.test.d.ts.map +1 -0
- package/dist/__tests__/errors.unit.test.d.ts +2 -0
- package/dist/__tests__/errors.unit.test.d.ts.map +1 -0
- package/dist/__tests__/generation-session.integration.test.d.ts +2 -0
- package/dist/__tests__/generation-session.integration.test.d.ts.map +1 -0
- package/dist/__tests__/generation-session.unit.test.d.ts +2 -0
- package/dist/__tests__/generation-session.unit.test.d.ts.map +1 -0
- package/dist/__tests__/key.unit.test.d.ts +2 -0
- package/dist/__tests__/key.unit.test.d.ts.map +1 -0
- package/dist/__tests__/layout.unit.test.d.ts +2 -0
- package/dist/__tests__/layout.unit.test.d.ts.map +1 -0
- package/dist/__tests__/lifecycle.unit.test.d.ts +2 -0
- package/dist/__tests__/lifecycle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/multiprocess-gc.integration.test.d.ts +2 -0
- package/dist/__tests__/multiprocess-gc.integration.test.d.ts.map +1 -0
- package/dist/__tests__/multiprocess-lifecycle.integration.test.d.ts +2 -0
- package/dist/__tests__/multiprocess-lifecycle.integration.test.d.ts.map +1 -0
- package/dist/__tests__/multiprocess-worker.d.ts +2 -0
- package/dist/__tests__/multiprocess-worker.d.ts.map +1 -0
- package/dist/__tests__/owner-chain.integration.test.d.ts +2 -0
- package/dist/__tests__/owner-chain.integration.test.d.ts.map +1 -0
- package/dist/__tests__/status-root-kind-owner.test-d.d.ts +2 -0
- package/dist/__tests__/status-root-kind-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/status.unit.test.d.ts +2 -0
- package/dist/__tests__/status.unit.test.d.ts.map +1 -0
- package/dist/build-cache.d.ts +18 -0
- package/dist/build-cache.d.ts.map +1 -0
- package/dist/entry-store.d.ts +53 -0
- package/dist/entry-store.d.ts.map +1 -0
- package/dist/entry-store.mjs +349 -0
- package/dist/entry-store.mjs.map +1 -0
- package/dist/errors.d.ts +58 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.mjs +109 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/gc.d.ts +13 -0
- package/dist/gc.d.ts.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +1574 -0
- package/dist/index.mjs.map +1 -0
- package/dist/key.d.ts +13 -0
- package/dist/key.d.ts.map +1 -0
- package/dist/key.mjs +38 -0
- package/dist/key.mjs.map +1 -0
- package/dist/layout.d.ts +51 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.mjs +68 -0
- package/dist/layout.mjs.map +1 -0
- package/dist/lifecycle.d.ts +93 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/publication.d.ts +50 -0
- package/dist/publication.d.ts.map +1 -0
- package/dist/runtime-scope.d.ts +10 -0
- package/dist/runtime-scope.d.ts.map +1 -0
- package/dist/session.d.ts +46 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/status.d.ts +39 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.mjs +20 -0
- package/dist/status.mjs.map +1 -0
- package/package.json +101 -0
- package/src/__tests__/concurrency.integration.test.ts +53 -0
- package/src/__tests__/consumer-path.integration.test.ts +61 -0
- package/src/__tests__/crash-recovery.integration.test.ts +84 -0
- package/src/__tests__/entry-store.integration.test.ts +83 -0
- package/src/__tests__/errors.unit.test.ts +54 -0
- package/src/__tests__/generation-session.integration.test.ts +202 -0
- package/src/__tests__/generation-session.unit.test.ts +35 -0
- package/src/__tests__/key.unit.test.ts +49 -0
- package/src/__tests__/layout.unit.test.ts +50 -0
- package/src/__tests__/lifecycle.unit.test.ts +258 -0
- package/src/__tests__/multiprocess-gc.integration.test.ts +36 -0
- package/src/__tests__/multiprocess-lifecycle.integration.test.ts +129 -0
- package/src/__tests__/multiprocess-worker.ts +41 -0
- package/src/__tests__/owner-chain.integration.test.ts +45 -0
- package/src/__tests__/status-root-kind-owner.test-d.ts +42 -0
- package/src/__tests__/status.unit.test.ts +56 -0
- package/src/build-cache.ts +60 -0
- package/src/entry-store.ts +317 -0
- package/src/errors.ts +164 -0
- package/src/gc.ts +34 -0
- package/src/index.ts +92 -0
- package/src/key.ts +46 -0
- package/src/layout.ts +132 -0
- package/src/lifecycle.ts +792 -0
- package/src/publication.ts +446 -0
- package/src/runtime-scope.ts +87 -0
- package/src/session.ts +281 -0
- package/src/status.ts +58 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export type DdcLifecycleState = 'missing' | 'cooking' | 'current' | 'stale' | 'failed';
|
|
2
|
+
export interface DdcLease {
|
|
3
|
+
readonly guid: string;
|
|
4
|
+
readonly desiredKey: string;
|
|
5
|
+
readonly attempt: string;
|
|
6
|
+
readonly generation: number;
|
|
7
|
+
readonly expectedRevision: number;
|
|
8
|
+
readonly instanceId: string;
|
|
9
|
+
readonly expiresAt: number;
|
|
10
|
+
}
|
|
11
|
+
export interface DdcHead {
|
|
12
|
+
readonly guid: string;
|
|
13
|
+
readonly desiredKey: string;
|
|
14
|
+
readonly state: DdcLifecycleState;
|
|
15
|
+
readonly currentKey: string | undefined;
|
|
16
|
+
readonly lastKnownGoodKey: string | undefined;
|
|
17
|
+
readonly revision?: number;
|
|
18
|
+
readonly generation?: number;
|
|
19
|
+
readonly activeLease?: DdcLease;
|
|
20
|
+
readonly failure?: {
|
|
21
|
+
readonly code: string;
|
|
22
|
+
readonly detail: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export interface DdcCommitResult {
|
|
26
|
+
readonly result: 'current' | 'stale' | 'lease-lost' | 'invalid';
|
|
27
|
+
readonly key: string;
|
|
28
|
+
readonly revision?: number;
|
|
29
|
+
/** Internal CAS evidence retained for a later generation rollback. */
|
|
30
|
+
readonly restoreFence?: DdcRestoreFence;
|
|
31
|
+
}
|
|
32
|
+
export interface DdcRestoreFence {
|
|
33
|
+
readonly attempt: string;
|
|
34
|
+
readonly generation: number;
|
|
35
|
+
readonly revision: number;
|
|
36
|
+
readonly desiredKey: string;
|
|
37
|
+
readonly outcome: 'current' | 'invalid' | 'failed';
|
|
38
|
+
readonly key?: string;
|
|
39
|
+
readonly currentKey?: string;
|
|
40
|
+
readonly lastKnownGoodKey?: string;
|
|
41
|
+
readonly failure?: {
|
|
42
|
+
readonly code: string;
|
|
43
|
+
readonly detail: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export interface DdcRestoreResult {
|
|
47
|
+
readonly result: 'restored' | 'not-owner';
|
|
48
|
+
readonly revision: number;
|
|
49
|
+
readonly generation?: number;
|
|
50
|
+
}
|
|
51
|
+
export type DdcRollbackSnapshot = Omit<DdcHead, 'activeLease'>;
|
|
52
|
+
export interface DdcBeginResult {
|
|
53
|
+
readonly lease: DdcLease;
|
|
54
|
+
readonly previousHead: DdcRollbackSnapshot;
|
|
55
|
+
}
|
|
56
|
+
/** Cross-process mkdir lock; a lock owned by a dead PID is reclaimable. */
|
|
57
|
+
export declare function withDdcLock<T>(root: string, name: string, operation: () => Promise<T>): Promise<T>;
|
|
58
|
+
export declare class DdcLifecycle {
|
|
59
|
+
private readonly heads;
|
|
60
|
+
private readonly entries;
|
|
61
|
+
private readonly root;
|
|
62
|
+
private readonly leaseTtlMs;
|
|
63
|
+
constructor(root: string, options?: {
|
|
64
|
+
readonly leaseTtlMs?: number;
|
|
65
|
+
});
|
|
66
|
+
inspect(guid: string, desiredKey: string): Promise<DdcHead>;
|
|
67
|
+
/** Begin a lease and capture the accepted rollback snapshot under one head lock. */
|
|
68
|
+
beginWithSnapshot(guid: string, desiredKey: string): Promise<DdcBeginResult>;
|
|
69
|
+
begin(guid: string, desiredKey: string): Promise<DdcLease>;
|
|
70
|
+
commit(lease: DdcLease, validatedKey: string): Promise<DdcCommitResult>;
|
|
71
|
+
fail(lease: DdcLease, failure: {
|
|
72
|
+
readonly code: string;
|
|
73
|
+
readonly detail: string;
|
|
74
|
+
}): Promise<DdcRestoreFence | undefined>;
|
|
75
|
+
heartbeat(lease: DdcLease): Promise<DdcLease>;
|
|
76
|
+
close(lease: DdcLease): Promise<void>;
|
|
77
|
+
discard(lease: DdcLease): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Restore accepted content only when this lease still owns the mutable head.
|
|
80
|
+
* A newer active or terminal mutation makes rollback a deliberate no-op.
|
|
81
|
+
*/
|
|
82
|
+
restore(head: DdcHead | DdcRollbackSnapshot, lease?: DdcLease, fence?: DdcRestoreFence): Promise<DdcRestoreResult>;
|
|
83
|
+
restoreIfCurrent(snapshot: DdcRollbackSnapshot, lease: DdcLease, fence?: DdcRestoreFence): Promise<DdcRestoreResult>;
|
|
84
|
+
revoke(lease: DdcLease): Promise<void>;
|
|
85
|
+
recover(guid: string, desiredKey: string): Promise<DdcHead>;
|
|
86
|
+
private rollbackSnapshot;
|
|
87
|
+
private matchesRestoreFence;
|
|
88
|
+
private projectHead;
|
|
89
|
+
private allocateGeneration;
|
|
90
|
+
private read;
|
|
91
|
+
private write;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { AssetPublicationEnvelope, AssetPublicationExternalEvidence, AssetPublicationFailure, AssetPublicationLocator, AssetPublicationOutput, AssetPublicationRecovery, Result } from '@forgeax/engine-types';
|
|
2
|
+
export interface ScriptablePackPublicationSnapshot {
|
|
3
|
+
readonly current?: AssetPublicationEnvelope;
|
|
4
|
+
readonly lastKnownGood?: AssetPublicationEnvelope;
|
|
5
|
+
readonly failure?: AssetPublicationFailure;
|
|
6
|
+
}
|
|
7
|
+
export interface ScriptablePackPublicationError extends AssetPublicationFailure {
|
|
8
|
+
readonly recovery: AssetPublicationRecovery;
|
|
9
|
+
readonly current?: AssetPublicationLocator;
|
|
10
|
+
readonly lastKnownGood?: AssetPublicationLocator;
|
|
11
|
+
}
|
|
12
|
+
export interface AcceptedPublicationCandidate {
|
|
13
|
+
readonly envelope: AssetPublicationEnvelope;
|
|
14
|
+
readonly cancelled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface ScriptablePackPublicationBuildInput {
|
|
17
|
+
readonly sourcePath: string;
|
|
18
|
+
readonly sourceRevision: string;
|
|
19
|
+
/** Optional caller generation; the DDC owner derives one when omitted. */
|
|
20
|
+
readonly generation?: number;
|
|
21
|
+
readonly digest: string;
|
|
22
|
+
readonly packageUrl: string;
|
|
23
|
+
readonly inputFingerprint: string;
|
|
24
|
+
readonly outputs: readonly AssetPublicationOutput[];
|
|
25
|
+
readonly externalEvidence: readonly AssetPublicationExternalEvidence[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* DDC-owned publication authority for source keyed producers. Transport
|
|
29
|
+
* adapters may stage bytes, but they do not retain current/LKG or generation
|
|
30
|
+
* state themselves.
|
|
31
|
+
*/
|
|
32
|
+
export interface AcceptedPublicationStore {
|
|
33
|
+
observe(sourcePath: string): ScriptablePackPublicationSnapshot;
|
|
34
|
+
stage(sourcePath: string, candidate: AcceptedPublicationCandidate): Result<void, ScriptablePackPublicationError>;
|
|
35
|
+
commit(sourcePath: string, candidate: AcceptedPublicationCandidate, commitRoute: () => Promise<void> | void): Promise<Result<ScriptablePackPublicationSnapshot, ScriptablePackPublicationError>>;
|
|
36
|
+
discard(sourcePath: string, candidate: AssetPublicationEnvelope): void;
|
|
37
|
+
restore(sourcePath: string, snapshot: ScriptablePackPublicationSnapshot): void;
|
|
38
|
+
}
|
|
39
|
+
export declare function scriptablePackOutputSetDigest(outputs: readonly AssetPublicationOutput[]): string;
|
|
40
|
+
/** Derive a stable positive generation from the accepted publication tuple. */
|
|
41
|
+
export declare function scriptablePackPublicationGeneration(input: {
|
|
42
|
+
readonly sourceRevision: string;
|
|
43
|
+
readonly digest: string;
|
|
44
|
+
readonly outputSetDigest: string;
|
|
45
|
+
}): number;
|
|
46
|
+
/** Build the complete producer-owned tuple after the package route is finalized. */
|
|
47
|
+
export declare function createAcceptedPublication(input: ScriptablePackPublicationBuildInput): AssetPublicationEnvelope;
|
|
48
|
+
/** Create the one DDC publication authority shared by dev attempts. */
|
|
49
|
+
export declare function createAcceptedPublicationStore(): AcceptedPublicationStore;
|
|
50
|
+
//# sourceMappingURL=publication.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publication.d.ts","sourceRoot":"","sources":["../src/publication.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,wBAAwB,EACxB,gCAAgC,EAChC,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,EACxB,MAAM,EACP,MAAM,uBAAuB,CAAC;AAG/B,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,OAAO,CAAC,EAAE,wBAAwB,CAAC;IAC5C,QAAQ,CAAC,aAAa,CAAC,EAAE,wBAAwB,CAAC;IAClD,QAAQ,CAAC,OAAO,CAAC,EAAE,uBAAuB,CAAC;CAC5C;AAED,MAAM,WAAW,8BAA+B,SAAQ,uBAAuB;IAC7E,QAAQ,CAAC,QAAQ,EAAE,wBAAwB,CAAC;IAC5C,QAAQ,CAAC,OAAO,CAAC,EAAE,uBAAuB,CAAC;IAC3C,QAAQ,CAAC,aAAa,CAAC,EAAE,uBAAuB,CAAC;CAClD;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,QAAQ,EAAE,wBAAwB,CAAC;IAC5C,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,mCAAmC;IAClD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACpD,QAAQ,CAAC,gBAAgB,EAAE,SAAS,gCAAgC,EAAE,CAAC;CACxE;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,iCAAiC,CAAC;IAC/D,KAAK,CACH,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,4BAA4B,GACtC,MAAM,CAAC,IAAI,EAAE,8BAA8B,CAAC,CAAC;IAChD,MAAM,CACJ,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,4BAA4B,EACvC,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GACtC,OAAO,CAAC,MAAM,CAAC,iCAAiC,EAAE,8BAA8B,CAAC,CAAC,CAAC;IACtF,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,wBAAwB,GAAG,IAAI,CAAC;IACvE,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,iCAAiC,GAAG,IAAI,CAAC;CAChF;AAkBD,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,SAAS,sBAAsB,EAAE,GAAG,MAAM,CAUhG;AAED,+EAA+E;AAC/E,wBAAgB,mCAAmC,CAAC,KAAK,EAAE;IACzD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC,GAAG,MAAM,CAUT;AAED,oFAAoF;AACpF,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,mCAAmC,GACzC,wBAAwB,CAmC1B;AAuPD,uEAAuE;AACvE,wBAAgB,8BAA8B,IAAI,wBAAwB,CAqDzE"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface DdcRuntimeScope {
|
|
2
|
+
readonly scopeId: string;
|
|
3
|
+
readonly scopeHash: string;
|
|
4
|
+
readonly root: string;
|
|
5
|
+
readonly metadataPath: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function runtimeScopeHash(scopeId: string): string;
|
|
8
|
+
export declare function createRuntimeScope(root: string, scopeId: string): Promise<DdcRuntimeScope>;
|
|
9
|
+
export declare function assertRuntimeScope(root: string, scope: Pick<DdcRuntimeScope, 'scopeId' | 'scopeHash'>): Promise<DdcRuntimeScope>;
|
|
10
|
+
//# sourceMappingURL=runtime-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-scope.d.ts","sourceRoot":"","sources":["../src/runtime-scope.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAYD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA0ChG;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,WAAW,CAAC,GACpD,OAAO,CAAC,eAAe,CAAC,CAa1B"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { DdcEntry, StagedDdcEntry } from './entry-store.js';
|
|
2
|
+
import type { DdcCommitResult, DdcHead, DdcLease, DdcRestoreResult, DdcRollbackSnapshot } from './lifecycle.js';
|
|
3
|
+
export interface DdcSessionOptions {
|
|
4
|
+
readonly generation: number;
|
|
5
|
+
readonly leaseTtlMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface DdcGenerationCandidate {
|
|
8
|
+
readonly generation: number;
|
|
9
|
+
readonly lease: DdcLease;
|
|
10
|
+
readonly previousHead: DdcRollbackSnapshot;
|
|
11
|
+
}
|
|
12
|
+
export interface DdcGenerationEntryCandidate extends DdcGenerationCandidate {
|
|
13
|
+
readonly staged: StagedDdcEntry;
|
|
14
|
+
}
|
|
15
|
+
export interface DdcSessionMetrics {
|
|
16
|
+
readonly hitCount: number;
|
|
17
|
+
readonly missCount: number;
|
|
18
|
+
readonly corruptCount: number;
|
|
19
|
+
readonly writeFailureCount: number;
|
|
20
|
+
}
|
|
21
|
+
export declare class DdcGenerationSession {
|
|
22
|
+
readonly generation: number;
|
|
23
|
+
private readonly lifecycle;
|
|
24
|
+
private readonly entries;
|
|
25
|
+
private readonly candidates;
|
|
26
|
+
private readonly restoreFences;
|
|
27
|
+
private readonly heartbeatTimers;
|
|
28
|
+
private readonly counters;
|
|
29
|
+
private accepting;
|
|
30
|
+
constructor(root: string, options: DdcSessionOptions);
|
|
31
|
+
beginCandidate(guid: string, desiredKey: string): Promise<DdcGenerationCandidate>;
|
|
32
|
+
stageEntry(entry: DdcEntry): Promise<DdcGenerationEntryCandidate>;
|
|
33
|
+
inspect(guid: string, desiredKey: string): Promise<DdcHead>;
|
|
34
|
+
commitCandidate(candidate: DdcGenerationCandidate, validatedKey: string): Promise<DdcCommitResult>;
|
|
35
|
+
commitEntry(candidate: DdcGenerationEntryCandidate, validatedKey: string): Promise<DdcCommitResult>;
|
|
36
|
+
discardCandidate(candidate: DdcGenerationCandidate): Promise<void>;
|
|
37
|
+
discardEntry(candidate: DdcGenerationEntryCandidate): Promise<void>;
|
|
38
|
+
restoreEntry(candidate: DdcGenerationEntryCandidate): Promise<DdcRestoreResult>;
|
|
39
|
+
metrics(): DdcSessionMetrics;
|
|
40
|
+
close(): Promise<void>;
|
|
41
|
+
private assertOpen;
|
|
42
|
+
private assertCandidate;
|
|
43
|
+
private scheduleHeartbeat;
|
|
44
|
+
private stopHeartbeat;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEjE,OAAO,KAAK,EACV,eAAe,EACf,OAAO,EACP,QAAQ,EAER,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAGxB,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;CAC5C;AAED,MAAM,WAAW,2BAA4B,SAAQ,sBAAsB;IACzE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC;AAmBD,qBAAa,oBAAoB;IAC/B,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoD;IAC/E,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0D;IACxF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqC;IACrE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAKvB;IACF,OAAO,CAAC,SAAS,CAAQ;gBAEN,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB;IAY9C,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAejF,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,2BAA2B,CAAC;IA0BjE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQ3D,eAAe,CAC1B,SAAS,EAAE,sBAAsB,EACjC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,eAAe,CAAC;IAiBd,WAAW,CACtB,SAAS,EAAE,2BAA2B,EACtC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,eAAe,CAAC;IAwBd,gBAAgB,CAAC,SAAS,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAalE,YAAY,CAAC,SAAS,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC;IAInE,YAAY,CAAC,SAAS,EAAE,2BAA2B,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2BrF,OAAO,IAAI,iBAAiB;IAItB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBnC,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,aAAa;CAOtB"}
|
package/dist/status.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { DdcErrorRootKind } from './errors.js';
|
|
2
|
+
export declare const DDC_STATUS_SCHEMA: "forgeax-ddc-status/v2";
|
|
3
|
+
export type DdcStatusHealth = 'ready' | 'blocked' | 'missing' | 'stale' | 'failed';
|
|
4
|
+
export type DdcStatusLayerKind = 'build' | 'project' | 'runtime';
|
|
5
|
+
export type DdcStatusRootKind = DdcErrorRootKind;
|
|
6
|
+
export interface DdcRecoveryAction {
|
|
7
|
+
readonly kind: 'inspect' | 'allocate-generation' | 'retry' | 'cold-rebuild' | 'prune';
|
|
8
|
+
readonly executable: boolean;
|
|
9
|
+
readonly exactTarget?: string;
|
|
10
|
+
readonly reason?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface DdcStatusLayer {
|
|
13
|
+
readonly kind: DdcStatusLayerKind;
|
|
14
|
+
readonly owner: 'engine-ddc' | 'engine-pack' | 'plugin' | 'runtime';
|
|
15
|
+
readonly rootKind: DdcStatusRootKind;
|
|
16
|
+
readonly scopeId?: string;
|
|
17
|
+
readonly current?: number | string;
|
|
18
|
+
readonly lastKnownGood?: number | string;
|
|
19
|
+
readonly protection: readonly string[];
|
|
20
|
+
readonly actions: readonly DdcRecoveryAction[];
|
|
21
|
+
}
|
|
22
|
+
export interface DdcStatus {
|
|
23
|
+
readonly schemaVersion: typeof DDC_STATUS_SCHEMA;
|
|
24
|
+
readonly health: DdcStatusHealth;
|
|
25
|
+
readonly layers: readonly DdcStatusLayer[];
|
|
26
|
+
readonly gameDir?: string;
|
|
27
|
+
readonly projectDdcRoot?: string;
|
|
28
|
+
}
|
|
29
|
+
export type BrowserDdcRecoveryAction = Omit<DdcRecoveryAction, 'exactTarget'>;
|
|
30
|
+
export type BrowserDdcStatusLayer = Omit<DdcStatusLayer, 'actions'> & {
|
|
31
|
+
readonly actions: readonly BrowserDdcRecoveryAction[];
|
|
32
|
+
};
|
|
33
|
+
export type BrowserDdcStatus = Omit<DdcStatus, 'gameDir' | 'projectDdcRoot' | 'layers'> & {
|
|
34
|
+
readonly layers: readonly BrowserDdcStatusLayer[];
|
|
35
|
+
};
|
|
36
|
+
export declare function serializeDdcStatus(status: DdcStatus): string;
|
|
37
|
+
export declare function projectDdcStatusForBrowser(status: DdcStatus): BrowserDdcStatus;
|
|
38
|
+
export declare const toBrowserDdcStatus: typeof projectDdcStatusForBrowser;
|
|
39
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,eAAO,MAAM,iBAAiB,EAAG,uBAAgC,CAAC;AAElE,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AACnF,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AACjE,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,qBAAqB,GAAG,OAAO,GAAG,cAAc,GAAG,OAAO,CAAC;IACtF,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,GAAG,QAAQ,GAAG,SAAS,CAAC;IACpE,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzC,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAChD;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,aAAa,EAAE,OAAO,iBAAiB,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;AAC9E,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,GAAG;IACpE,QAAQ,CAAC,OAAO,EAAE,SAAS,wBAAwB,EAAE,CAAC;CACvD,CAAC;AACF,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,GAAG,gBAAgB,GAAG,QAAQ,CAAC,GAAG;IACxF,QAAQ,CAAC,MAAM,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACnD,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAE5D;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,GAAG,gBAAgB,CAS9E;AAED,eAAO,MAAM,kBAAkB,mCAA6B,CAAC"}
|
package/dist/status.mjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// src/status.ts
|
|
2
|
+
var DDC_STATUS_SCHEMA = "forgeax-ddc-status/v2";
|
|
3
|
+
function serializeDdcStatus(status) {
|
|
4
|
+
return JSON.stringify(status);
|
|
5
|
+
}
|
|
6
|
+
function projectDdcStatusForBrowser(status) {
|
|
7
|
+
return {
|
|
8
|
+
schemaVersion: DDC_STATUS_SCHEMA,
|
|
9
|
+
health: status.health,
|
|
10
|
+
layers: status.layers.map((layer) => ({
|
|
11
|
+
...layer,
|
|
12
|
+
actions: layer.actions.map(({ exactTarget: _exactTarget, ...action }) => action)
|
|
13
|
+
}))
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
var toBrowserDdcStatus = projectDdcStatusForBrowser;
|
|
17
|
+
|
|
18
|
+
export { DDC_STATUS_SCHEMA, projectDdcStatusForBrowser, serializeDdcStatus, toBrowserDdcStatus };
|
|
19
|
+
//# sourceMappingURL=status.mjs.map
|
|
20
|
+
//# sourceMappingURL=status.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/status.ts"],"names":[],"mappings":";AAEO,IAAM,iBAAA,GAAoB;AAwC1B,SAAS,mBAAmB,MAAA,EAA2B;AAC5D,EAAA,OAAO,IAAA,CAAK,UAAU,MAAM,CAAA;AAC9B;AAEO,SAAS,2BAA2B,MAAA,EAAqC;AAC9E,EAAA,OAAO;AAAA,IACL,aAAA,EAAe,iBAAA;AAAA,IACf,QAAQ,MAAA,CAAO,MAAA;AAAA,IACf,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,MACpC,GAAG,KAAA;AAAA,MACH,OAAA,EAAS,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,EAAE,WAAA,EAAa,YAAA,EAAc,GAAG,MAAA,EAAO,KAAM,MAAM;AAAA,KACjF,CAAE;AAAA,GACJ;AACF;AAEO,IAAM,kBAAA,GAAqB","file":"status.mjs","sourcesContent":["import type { DdcErrorRootKind } from './errors.js';\n\nexport const DDC_STATUS_SCHEMA = 'forgeax-ddc-status/v2' as const;\n\nexport type DdcStatusHealth = 'ready' | 'blocked' | 'missing' | 'stale' | 'failed';\nexport type DdcStatusLayerKind = 'build' | 'project' | 'runtime';\nexport type DdcStatusRootKind = DdcErrorRootKind;\n\nexport interface DdcRecoveryAction {\n readonly kind: 'inspect' | 'allocate-generation' | 'retry' | 'cold-rebuild' | 'prune';\n readonly executable: boolean;\n readonly exactTarget?: string;\n readonly reason?: string;\n}\n\nexport interface DdcStatusLayer {\n readonly kind: DdcStatusLayerKind;\n readonly owner: 'engine-ddc' | 'engine-pack' | 'plugin' | 'runtime';\n readonly rootKind: DdcStatusRootKind;\n readonly scopeId?: string;\n readonly current?: number | string;\n readonly lastKnownGood?: number | string;\n readonly protection: readonly string[];\n readonly actions: readonly DdcRecoveryAction[];\n}\n\nexport interface DdcStatus {\n readonly schemaVersion: typeof DDC_STATUS_SCHEMA;\n readonly health: DdcStatusHealth;\n readonly layers: readonly DdcStatusLayer[];\n readonly gameDir?: string;\n readonly projectDdcRoot?: string;\n}\n\nexport type BrowserDdcRecoveryAction = Omit<DdcRecoveryAction, 'exactTarget'>;\nexport type BrowserDdcStatusLayer = Omit<DdcStatusLayer, 'actions'> & {\n readonly actions: readonly BrowserDdcRecoveryAction[];\n};\nexport type BrowserDdcStatus = Omit<DdcStatus, 'gameDir' | 'projectDdcRoot' | 'layers'> & {\n readonly layers: readonly BrowserDdcStatusLayer[];\n};\n\nexport function serializeDdcStatus(status: DdcStatus): string {\n return JSON.stringify(status);\n}\n\nexport function projectDdcStatusForBrowser(status: DdcStatus): BrowserDdcStatus {\n return {\n schemaVersion: DDC_STATUS_SCHEMA,\n health: status.health,\n layers: status.layers.map((layer) => ({\n ...layer,\n actions: layer.actions.map(({ exactTarget: _exactTarget, ...action }) => action),\n })),\n };\n}\n\nexport const toBrowserDdcStatus = projectDdcStatusForBrowser;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@forgeax/engine-ddc",
|
|
3
|
+
"version": "0.0.0-dev.8d955ade1c79",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"description": "Node-only v2 immutable derived data cache with project-local lifecycle evidence.",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"node": {
|
|
13
|
+
"import": "./dist/index.mjs"
|
|
14
|
+
},
|
|
15
|
+
"default": null
|
|
16
|
+
},
|
|
17
|
+
"./key": {
|
|
18
|
+
"types": "./dist/key.d.ts",
|
|
19
|
+
"node": {
|
|
20
|
+
"import": "./dist/key.mjs"
|
|
21
|
+
},
|
|
22
|
+
"default": null
|
|
23
|
+
},
|
|
24
|
+
"./entry-store": {
|
|
25
|
+
"types": "./dist/entry-store.d.ts",
|
|
26
|
+
"node": {
|
|
27
|
+
"import": "./dist/entry-store.mjs"
|
|
28
|
+
},
|
|
29
|
+
"default": null
|
|
30
|
+
},
|
|
31
|
+
"./layout": {
|
|
32
|
+
"types": "./dist/layout.d.ts",
|
|
33
|
+
"node": {
|
|
34
|
+
"import": "./dist/layout.mjs"
|
|
35
|
+
},
|
|
36
|
+
"default": null
|
|
37
|
+
},
|
|
38
|
+
"./status": {
|
|
39
|
+
"types": "./dist/status.d.ts",
|
|
40
|
+
"node": {
|
|
41
|
+
"import": "./dist/status.mjs"
|
|
42
|
+
},
|
|
43
|
+
"default": null
|
|
44
|
+
},
|
|
45
|
+
"./errors": {
|
|
46
|
+
"types": "./dist/errors.d.ts",
|
|
47
|
+
"node": {
|
|
48
|
+
"import": "./dist/errors.mjs"
|
|
49
|
+
},
|
|
50
|
+
"default": null
|
|
51
|
+
},
|
|
52
|
+
"./package.json": "./package.json"
|
|
53
|
+
},
|
|
54
|
+
"main": "./dist/index.mjs",
|
|
55
|
+
"types": "./dist/index.d.ts",
|
|
56
|
+
"files": [
|
|
57
|
+
"dist",
|
|
58
|
+
"src",
|
|
59
|
+
"README.md",
|
|
60
|
+
"LICENSE"
|
|
61
|
+
],
|
|
62
|
+
"forgeax": {
|
|
63
|
+
"metrics": {
|
|
64
|
+
"bundle-size": {
|
|
65
|
+
"enabled": true,
|
|
66
|
+
"path": "dist/index.mjs",
|
|
67
|
+
"compression": "gzip"
|
|
68
|
+
},
|
|
69
|
+
"fps": {
|
|
70
|
+
"enabled": false,
|
|
71
|
+
"reason": "Node-only build-time cache package; no runtime frame loop"
|
|
72
|
+
},
|
|
73
|
+
"bench": {
|
|
74
|
+
"enabled": false,
|
|
75
|
+
"reason": "DDC transaction correctness is covered by integration tests; no benchmark contract is declared"
|
|
76
|
+
},
|
|
77
|
+
"gate": {
|
|
78
|
+
"enabled": false,
|
|
79
|
+
"reason": "No package-level binary gate; workspace CI owns cache audit and type gates"
|
|
80
|
+
},
|
|
81
|
+
"spike-report": {
|
|
82
|
+
"enabled": false,
|
|
83
|
+
"reason": "Not a spike package"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"dependencies": {
|
|
88
|
+
"@forgeax/engine-types": "0.0.0-dev.8d955ade1c79"
|
|
89
|
+
},
|
|
90
|
+
"devDependencies": {
|
|
91
|
+
"@types/node": "^20.14.0",
|
|
92
|
+
"tsup": "^8.3.5",
|
|
93
|
+
"typescript": "^5.8.3",
|
|
94
|
+
"vitest": "4.1.5"
|
|
95
|
+
},
|
|
96
|
+
"scripts": {
|
|
97
|
+
"build": "tsup",
|
|
98
|
+
"typecheck": "tsc -b",
|
|
99
|
+
"test": "vitest run"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { type DdcEntry, DdcEntryStore, ddcOutputDigest } from '../entry-store.js';
|
|
6
|
+
|
|
7
|
+
const KEY = 'a'.repeat(64);
|
|
8
|
+
const entry = (payload: string): DdcEntry => {
|
|
9
|
+
const value = {
|
|
10
|
+
key: KEY,
|
|
11
|
+
guid: '019e3969-1d48-7c3b-ac24-6d68f457065f',
|
|
12
|
+
payload: { payload },
|
|
13
|
+
refs: [],
|
|
14
|
+
artifacts: {
|
|
15
|
+
data: { mediaType: 'application/octet-stream', bytes: new Uint8Array([1, 2, 3]) },
|
|
16
|
+
},
|
|
17
|
+
receipt: {
|
|
18
|
+
guid: '019e3969-1d48-7c3b-ac24-6d68f457065f',
|
|
19
|
+
key: KEY,
|
|
20
|
+
producer: 'test-producer',
|
|
21
|
+
inputFingerprint: 'input',
|
|
22
|
+
outputDigest: '',
|
|
23
|
+
},
|
|
24
|
+
} satisfies DdcEntry;
|
|
25
|
+
return { ...value, receipt: { ...value.receipt, outputDigest: ddcOutputDigest(value) } };
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
describe('DDC same-key writer concurrency', () => {
|
|
29
|
+
const roots: string[] = [];
|
|
30
|
+
|
|
31
|
+
afterEach(async () => {
|
|
32
|
+
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('lets one writer publish and makes the loser observe an identical entry', async () => {
|
|
36
|
+
const root = await mkdtemp(join(tmpdir(), 'forgeax-ddc-concurrency-'));
|
|
37
|
+
roots.push(root);
|
|
38
|
+
const store = new DdcEntryStore(root);
|
|
39
|
+
const results = await Promise.all([store.write(entry('same')), store.write(entry('same'))]);
|
|
40
|
+
|
|
41
|
+
expect(results.map((result) => result.result).sort()).toEqual(['existing', 'published']);
|
|
42
|
+
await expect(store.read(KEY)).resolves.toEqual(entry('same'));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('reports a conflict when the same key maps to different bytes', async () => {
|
|
46
|
+
const root = await mkdtemp(join(tmpdir(), 'forgeax-ddc-concurrency-'));
|
|
47
|
+
roots.push(root);
|
|
48
|
+
const store = new DdcEntryStore(root);
|
|
49
|
+
await store.write(entry('first'));
|
|
50
|
+
await expect(store.write(entry('second'))).resolves.toEqual({ result: 'conflict', key: KEY });
|
|
51
|
+
await expect(store.read(KEY)).resolves.toEqual(entry('first'));
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { type DdcEntry, DdcEntryStore, ddcOutputDigest } from '../entry-store.js';
|
|
6
|
+
|
|
7
|
+
const guid = '019e3969-1d48-7c3b-ac24-6d68f457065f';
|
|
8
|
+
const key = 'a'.repeat(64);
|
|
9
|
+
|
|
10
|
+
describe('DDC consumer path', () => {
|
|
11
|
+
const roots: string[] = [];
|
|
12
|
+
|
|
13
|
+
afterEach(async () => {
|
|
14
|
+
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('rebuilds the same output after deleting DDC storage', async () => {
|
|
18
|
+
const firstRoot = await mkdtemp(join(tmpdir(), 'forgeax-ddc-consumer-'));
|
|
19
|
+
const secondRoot = await mkdtemp(join(tmpdir(), 'forgeax-ddc-consumer-'));
|
|
20
|
+
roots.push(firstRoot, secondRoot);
|
|
21
|
+
const cookedBase = {
|
|
22
|
+
key,
|
|
23
|
+
guid,
|
|
24
|
+
payload: { kind: 'texture', width: 2, height: 1 },
|
|
25
|
+
refs: [],
|
|
26
|
+
artifacts: {
|
|
27
|
+
payload: { mediaType: 'application/octet-stream', bytes: new Uint8Array([1, 2]) },
|
|
28
|
+
},
|
|
29
|
+
receipt: {
|
|
30
|
+
guid,
|
|
31
|
+
key,
|
|
32
|
+
producer: 'image-importer@4',
|
|
33
|
+
inputFingerprint: 'source-bytes',
|
|
34
|
+
outputDigest: '',
|
|
35
|
+
},
|
|
36
|
+
} satisfies DdcEntry;
|
|
37
|
+
const cooked: DdcEntry = {
|
|
38
|
+
...cookedBase,
|
|
39
|
+
receipt: { ...cookedBase.receipt, outputDigest: ddcOutputDigest(cookedBase) },
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const firstStore = new DdcEntryStore(firstRoot);
|
|
43
|
+
await firstStore.write(cooked);
|
|
44
|
+
const cold = await firstStore.read(key);
|
|
45
|
+
await rm(firstRoot, { recursive: true, force: true });
|
|
46
|
+
const secondStore = new DdcEntryStore(secondRoot);
|
|
47
|
+
await secondStore.write(cooked);
|
|
48
|
+
const rebuilt = await secondStore.read(key);
|
|
49
|
+
|
|
50
|
+
expect(rebuilt).toEqual(cold);
|
|
51
|
+
expect(rebuilt?.guid).toBe(guid);
|
|
52
|
+
expect(rebuilt?.receipt.outputDigest).toBe(ddcOutputDigest(cooked));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('keeps a direct product path independent from DDC presence', async () => {
|
|
56
|
+
const root = await mkdtemp(join(tmpdir(), 'forgeax-ddc-direct-'));
|
|
57
|
+
roots.push(root);
|
|
58
|
+
const store = new DdcEntryStore(root);
|
|
59
|
+
await expect(store.read(key)).resolves.toBeNull();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { DdcEntryStore, ddcOutputDigest } from '../entry-store.js';
|
|
6
|
+
import { DdcLifecycle } from '../lifecycle.js';
|
|
7
|
+
|
|
8
|
+
const GUID = '019e3969-1d48-7c3b-ac24-6d68f457065f';
|
|
9
|
+
const KEY_A = 'a'.repeat(64);
|
|
10
|
+
const KEY_B = 'b'.repeat(64);
|
|
11
|
+
|
|
12
|
+
async function writeEntry(root: string, key: string): Promise<void> {
|
|
13
|
+
const base = {
|
|
14
|
+
key,
|
|
15
|
+
guid: GUID,
|
|
16
|
+
payload: { key },
|
|
17
|
+
refs: [],
|
|
18
|
+
artifacts: {},
|
|
19
|
+
receipt: {
|
|
20
|
+
guid: GUID,
|
|
21
|
+
key,
|
|
22
|
+
producer: 'test',
|
|
23
|
+
inputFingerprint: key,
|
|
24
|
+
outputDigest: '',
|
|
25
|
+
},
|
|
26
|
+
} as const;
|
|
27
|
+
await new DdcEntryStore(root).write({
|
|
28
|
+
...base,
|
|
29
|
+
receipt: { ...base.receipt, outputDigest: ddcOutputDigest(base) },
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe('DDC lease and crash recovery', () => {
|
|
34
|
+
const roots: string[] = [];
|
|
35
|
+
|
|
36
|
+
afterEach(async () => {
|
|
37
|
+
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('keeps a crashed cooking attempt out of the readable head', async () => {
|
|
41
|
+
const root = await mkdtemp(join(tmpdir(), 'forgeax-ddc-crash-'));
|
|
42
|
+
roots.push(root);
|
|
43
|
+
const lifecycle = new DdcLifecycle(root);
|
|
44
|
+
await lifecycle.begin(GUID, KEY_A);
|
|
45
|
+
|
|
46
|
+
await expect(lifecycle.inspect(GUID, KEY_A)).resolves.toMatchObject({ state: 'cooking' });
|
|
47
|
+
await expect(lifecycle.recover(GUID, KEY_A)).resolves.toMatchObject({ state: 'failed' });
|
|
48
|
+
await expect(lifecycle.inspect(GUID, KEY_A)).resolves.toMatchObject({
|
|
49
|
+
state: 'failed',
|
|
50
|
+
currentKey: undefined,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('rejects a commit after the lease is lost', async () => {
|
|
55
|
+
const root = await mkdtemp(join(tmpdir(), 'forgeax-ddc-crash-'));
|
|
56
|
+
roots.push(root);
|
|
57
|
+
const lifecycle = new DdcLifecycle(root);
|
|
58
|
+
const lease = await lifecycle.begin(GUID, KEY_A);
|
|
59
|
+
await lifecycle.revoke(lease);
|
|
60
|
+
|
|
61
|
+
await expect(lifecycle.commit(lease, KEY_A)).resolves.toEqual({
|
|
62
|
+
result: 'lease-lost',
|
|
63
|
+
key: KEY_A,
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('keeps an old writer stale after a newer desired key starts cooking', async () => {
|
|
68
|
+
const root = await mkdtemp(join(tmpdir(), 'forgeax-ddc-crash-'));
|
|
69
|
+
roots.push(root);
|
|
70
|
+
const lifecycle = new DdcLifecycle(root);
|
|
71
|
+
const oldLease = await lifecycle.begin(GUID, KEY_A);
|
|
72
|
+
const newLease = await lifecycle.begin(GUID, KEY_B);
|
|
73
|
+
await writeEntry(root, KEY_B);
|
|
74
|
+
|
|
75
|
+
await expect(lifecycle.commit(oldLease, KEY_A)).resolves.toEqual({
|
|
76
|
+
result: 'stale',
|
|
77
|
+
key: KEY_A,
|
|
78
|
+
});
|
|
79
|
+
await expect(lifecycle.commit(newLease, KEY_B)).resolves.toEqual({
|
|
80
|
+
result: 'current',
|
|
81
|
+
key: KEY_B,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|