@evomap/evolver-core 2.0.0-beta.2 → 2.0.0-beta.22
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/assets/gep/genes.jsonl +5 -5
- package/dist/algo/bans.d.ts +10 -1
- package/dist/algo/bans.js +56 -6
- package/dist/algo/candidateAssembly.d.ts +25 -2
- package/dist/algo/candidateAssembly.js +171 -26
- package/dist/algo/capabilityCandidates.js +10 -0
- package/dist/algo/conversationSniffer.js +25 -8
- package/dist/algo/cycleEngine.d.ts +63 -5
- package/dist/algo/cycleEngine.js +320 -42
- package/dist/algo/cycleFailureClassifier.d.ts +1 -1
- package/dist/algo/evolutionEvent.d.ts +26 -0
- package/dist/algo/evolutionEvent.js +32 -0
- package/dist/algo/exploration.d.ts +7 -0
- package/dist/algo/exploration.js +16 -3
- package/dist/algo/geneHealth.d.ts +36 -3
- package/dist/algo/geneHealth.js +47 -4
- package/dist/algo/geneIntake.d.ts +46 -1
- package/dist/algo/geneIntake.js +121 -10
- package/dist/algo/geneSelection.d.ts +126 -6
- package/dist/algo/geneSelection.js +479 -36
- package/dist/algo/index.d.ts +6 -1
- package/dist/algo/index.js +6 -1
- package/dist/algo/kautoProjection.d.ts +41 -0
- package/dist/algo/kautoProjection.js +95 -0
- package/dist/algo/kautoValidator.d.ts +68 -0
- package/dist/algo/kautoValidator.js +256 -0
- package/dist/algo/memoryGraph.d.ts +62 -0
- package/dist/algo/memoryGraph.js +86 -0
- package/dist/algo/orchestrator.d.ts +17 -1
- package/dist/algo/orchestrator.js +30 -4
- package/dist/algo/publishEligibility.d.ts +34 -0
- package/dist/algo/publishEligibility.js +52 -0
- package/dist/algo/solidify.d.ts +11 -2
- package/dist/algo/solidify.js +37 -7
- package/dist/algo/ucb1.d.ts +53 -0
- package/dist/algo/ucb1.js +156 -0
- package/dist/assetrepair/hubRejection.d.ts +12 -0
- package/dist/assetrepair/hubRejection.js +109 -0
- package/dist/assetrepair/index.d.ts +2 -0
- package/dist/assetrepair/index.js +2 -0
- package/dist/assetrepair/repair.d.ts +33 -0
- package/dist/assetrepair/repair.js +155 -0
- package/dist/assetstore/assetSidecarRecords.d.ts +28 -0
- package/dist/assetstore/assetSidecarRecords.js +384 -0
- package/dist/assetstore/assetSidecarRecovery.d.ts +48 -0
- package/dist/assetstore/assetSidecarRecovery.js +288 -0
- package/dist/assetstore/assetStoreHealth.d.ts +81 -0
- package/dist/assetstore/assetStoreHealth.js +319 -0
- package/dist/assetstore/assetStoreLayout.d.ts +2 -0
- package/dist/assetstore/assetStoreLayout.js +6 -0
- package/dist/assetstore/assetStoreStorage.d.ts +42 -0
- package/dist/assetstore/assetStoreStorage.js +336 -0
- package/dist/assetstore/assetSyncLedger.d.ts +91 -1
- package/dist/assetstore/assetSyncLedger.js +718 -59
- package/dist/assetstore/foreignJsonlSource.d.ts +48 -0
- package/dist/assetstore/foreignJsonlSource.js +150 -0
- package/dist/assetstore/index.d.ts +5 -0
- package/dist/assetstore/index.js +5 -0
- package/dist/assetstore/learningHistory.js +3 -3
- package/dist/assetstore/localAssetStoreSnapshot.d.ts +51 -0
- package/dist/assetstore/localAssetStoreSnapshot.js +329 -0
- package/dist/assetstore/localJsonl.d.ts +12 -1
- package/dist/assetstore/localJsonl.js +251 -42
- package/dist/assetstore/provenance.d.ts +93 -4
- package/dist/assetstore/provenance.js +368 -84
- package/dist/assetstore/provider.d.ts +63 -0
- package/dist/assetstore/provider.js +97 -6
- package/dist/assetstore/reviewFilter.d.ts +19 -1
- package/dist/assetstore/reviewFilter.js +39 -1
- package/dist/assetstore/reviewLedger.d.ts +8 -2
- package/dist/assetstore/reviewLedger.js +71 -45
- package/dist/assetstore/unionReadStore.d.ts +25 -0
- package/dist/assetstore/unionReadStore.js +119 -0
- package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
- package/dist/benchmark/antiGeneBenchmark.js +4 -3
- package/dist/benchmark/antiGeneRollout.d.ts +2 -0
- package/dist/benchmark/antiGeneRollout.js +4 -3
- package/dist/benchmark/index.d.ts +3 -1
- package/dist/benchmark/index.js +3 -1
- package/dist/benchmark/selectionFlatAbstention.d.ts +152 -0
- package/dist/benchmark/selectionFlatAbstention.js +481 -0
- package/dist/benchmark/triggerShift.d.ts +62 -0
- package/dist/benchmark/triggerShift.js +106 -0
- package/dist/bootstrap/envFingerprint.d.ts +9 -0
- package/dist/bootstrap/envFingerprint.js +5 -0
- package/dist/bootstrap/index.d.ts +3 -1
- package/dist/bootstrap/index.js +3 -1
- package/dist/bootstrap/lifecycleBootstrap.d.ts +111 -0
- package/dist/bootstrap/lifecycleBootstrap.js +433 -0
- package/dist/bootstrap/v1EnvCompat.d.ts +113 -0
- package/dist/bootstrap/v1EnvCompat.js +303 -0
- package/dist/events/eventArchive.d.ts +2 -0
- package/dist/events/eventArchive.js +13 -3
- package/dist/events/eventSchema.d.ts +7 -7
- package/dist/events/eventStore.d.ts +2 -0
- package/dist/events/eventStore.js +5 -1
- package/dist/events/ingest.d.ts +2 -1
- package/dist/events/ingest.js +15 -0
- package/dist/events/paths.d.ts +12 -10
- package/dist/events/paths.js +24 -20
- package/dist/events/public.d.ts +2 -2
- package/dist/events/public.js +2 -2
- package/dist/events/reports.d.ts +2 -0
- package/dist/events/reports.js +4 -0
- package/dist/exec/autoExec.d.ts +68 -4
- package/dist/exec/autoExec.js +392 -33
- package/dist/exec/autonomousCycle.d.ts +31 -4
- package/dist/exec/autonomousCycle.js +71 -13
- package/dist/exec/claudeBridge.d.ts +80 -15
- package/dist/exec/claudeBridge.js +845 -76
- package/dist/exec/executionBinding.d.ts +414 -0
- package/dist/exec/executionBinding.js +588 -0
- package/dist/exec/index.d.ts +1 -0
- package/dist/exec/index.js +1 -0
- package/dist/exec/openPrRegistry.d.ts +8 -2
- package/dist/exec/openPrRegistry.js +32 -22
- package/dist/exec/prompt.js +14 -1
- package/dist/exec/proofOfWork.d.ts +1 -1
- package/dist/exec/proofOfWork.js +2 -2
- package/dist/exec/runnerRegistry.d.ts +153 -36
- package/dist/exec/runnerRegistry.js +848 -65
- package/dist/exec/selfPr.js +1 -7
- package/dist/feedback/envelope.d.ts +61 -0
- package/dist/feedback/envelope.js +168 -0
- package/dist/feedback/index.d.ts +1 -0
- package/dist/feedback/index.js +1 -0
- package/dist/hooks/hooks.js +1 -0
- package/dist/hub/assetCallLog.d.ts +35 -1
- package/dist/hub/assetCallLog.js +124 -1
- package/dist/hub/bindings.d.ts +8 -1
- package/dist/hub/bindings.js +29 -8
- package/dist/hub/capability.d.ts +130 -4
- package/dist/hub/conversationDistiller.d.ts +19 -0
- package/dist/hub/conversationDistiller.js +115 -37
- package/dist/hub/fake.d.ts +3 -2
- package/dist/hub/fake.js +2 -1
- package/dist/hub/index.d.ts +1 -0
- package/dist/hub/index.js +1 -0
- package/dist/hub/questionGenerator.d.ts +5 -1
- package/dist/hub/questionGenerator.js +8 -6
- package/dist/hub/recipeCompose.d.ts +27 -0
- package/dist/hub/recipeCompose.js +90 -0
- package/dist/hub/sanitize.js +122 -7
- package/dist/index.d.ts +6 -1
- package/dist/index.js +7 -1
- package/dist/issueReporter/index.d.ts +156 -0
- package/dist/issueReporter/index.js +1688 -0
- package/dist/mailbox/dispatch.d.ts +1 -1
- package/dist/mailbox/dispatch.js +22 -6
- package/dist/mailbox/envelope.d.ts +7 -1
- package/dist/mailbox/envelope.js +9 -2
- package/dist/mailbox/ipcServer.d.ts +12 -2
- package/dist/mailbox/ipcServer.js +183 -13
- package/dist/mailbox/store.d.ts +89 -3
- package/dist/mailbox/store.js +895 -41
- package/dist/modelCompatibility.d.ts +164 -0
- package/dist/modelCompatibility.js +309 -0
- package/dist/observers/valueDigestObserver.d.ts +9 -0
- package/dist/observers/valueDigestObserver.js +35 -2
- package/dist/ops/cleanup.js +1 -1
- package/dist/ops/evolutionGraphProjection.d.ts +20 -0
- package/dist/ops/evolutionGraphProjection.js +315 -0
- package/dist/ops/index.d.ts +2 -1
- package/dist/ops/index.js +2 -1
- package/dist/ops/savingsCore.js +1 -2
- package/dist/ops/selfUpdate.d.ts +18 -1
- package/dist/ops/selfUpdate.js +88 -23
- package/dist/ops/valueOutreach.d.ts +3 -1
- package/dist/ops/valueOutreach.js +5 -1
- package/dist/personality/schema.d.ts +24 -24
- package/dist/schema/evolutionGraph.d.ts +784 -0
- package/dist/schema/evolutionGraph.js +187 -0
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/proofOfWork.d.ts +125 -6
- package/dist/schema/proofOfWork.js +102 -4
- package/dist/schema/signal.d.ts +3 -3
- package/dist/schema/signal.js +1 -1
- package/dist/shadow/shadowHub.js +1 -0
- package/dist/signals/curriculum.d.ts +55 -0
- package/dist/signals/curriculum.js +202 -0
- package/dist/signals/cycleHistoryFromEvents.js +17 -8
- package/dist/signals/expand.d.ts +15 -1
- package/dist/signals/expand.js +169 -1
- package/dist/signals/extractor.d.ts +2 -2
- package/dist/signals/extractor.js +31 -6
- package/dist/signals/index.d.ts +4 -1
- package/dist/signals/index.js +4 -1
- package/dist/signals/metaSignals.d.ts +4 -0
- package/dist/signals/metaSignals.js +42 -0
- package/dist/signals/scopeVocabulary.d.ts +75 -0
- package/dist/signals/scopeVocabulary.js +91 -0
- package/dist/signals/signalGate.js +1 -1
- package/dist/signals/taskDomain.d.ts +22 -0
- package/dist/signals/taskDomain.js +43 -0
- package/dist/strategy/constraintAblation.d.ts +64 -0
- package/dist/strategy/constraintAblation.js +2820 -0
- package/dist/strategy/constraintAblationPredicates.d.ts +31 -0
- package/dist/strategy/constraintAblationPredicates.js +339 -0
- package/dist/strategy/index.d.ts +2 -1
- package/dist/strategy/index.js +2 -1
- package/dist/trace/index.d.ts +3 -1
- package/dist/trace/index.js +3 -1
- package/dist/trace/learningTrace.d.ts +216 -0
- package/dist/trace/learningTrace.js +298 -0
- package/dist/trace/proxyTurns.d.ts +31 -0
- package/dist/trace/proxyTurns.js +137 -0
- package/dist/trace/trajectory.d.ts +8 -0
- package/dist/trace/trajectory.js +14 -2
- package/dist/util/fetchPort.d.ts +1 -0
- package/dist/util/fetchPort.js +11 -0
- package/dist/util/fileLock.d.ts +113 -7
- package/dist/util/fileLock.js +1035 -80
- package/dist/util/index.d.ts +3 -1
- package/dist/util/index.js +2 -1
- package/dist/verify/index.d.ts +2 -1
- package/dist/verify/index.js +1 -1
- package/dist/verify/sandboxRunner.d.ts +30 -0
- package/dist/verify/sandboxRunner.js +370 -27
- package/dist/verify/sandboxedValidation.d.ts +16 -2
- package/dist/verify/sandboxedValidation.js +200 -22
- package/dist/verify/validation.d.ts +35 -4
- package/dist/verify/validation.js +164 -16
- package/dist/wire/geneHints.d.ts +88 -16
- package/dist/wire/geneHints.js +124 -15
- package/dist/wire/index.d.ts +8 -3
- package/dist/wire/index.js +2 -2
- package/dist/wire/schemaGate.d.ts +21 -0
- package/dist/wire/schemaGate.js +119 -10
- package/dist/workflow/dsl.d.ts +24 -3
- package/dist/workflow/dsl.js +4 -0
- package/dist/workflow/engine.d.ts +5 -1
- package/dist/workflow/engine.js +3 -0
- package/dist/workflow/index.d.ts +3 -1
- package/dist/workflow/index.js +3 -1
- package/dist/workflow/runtime.d.ts +110 -0
- package/dist/workflow/runtime.js +1298 -0
- package/dist/workflow/stateStore.d.ts +172 -0
- package/dist/workflow/stateStore.js +1044 -0
- package/package.json +12 -4
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Stats } from 'node:fs';
|
|
2
|
+
import { acquireLock, releaseLock, type ReleaseLockResult } from '../util/fileLock.js';
|
|
3
|
+
export type UnsafeAssetStorePathReason = 'symlink' | 'not_directory' | 'not_regular_file' | 'path_changed';
|
|
4
|
+
export type AssetStorePathRole = 'base_directory' | 'asset_file' | 'lock_file' | 'temp_file';
|
|
5
|
+
export declare class UnsafeAssetStorePathError extends Error {
|
|
6
|
+
readonly role: AssetStorePathRole;
|
|
7
|
+
readonly reason: UnsafeAssetStorePathReason;
|
|
8
|
+
readonly code = "UNSAFE_ASSET_STORE_PATH";
|
|
9
|
+
constructor(role: AssetStorePathRole, reason: UnsafeAssetStorePathReason);
|
|
10
|
+
}
|
|
11
|
+
export declare class AssetStoreReadLimitError extends Error {
|
|
12
|
+
readonly code = "ASSET_STORE_READ_LIMIT";
|
|
13
|
+
constructor();
|
|
14
|
+
}
|
|
15
|
+
export interface DurableWriteOptions {
|
|
16
|
+
syncFile?: (fd: number) => void;
|
|
17
|
+
syncDirectory?: (path: string) => void;
|
|
18
|
+
onTempPath?: (path: string) => void;
|
|
19
|
+
}
|
|
20
|
+
export interface AssetStoreLockDeps {
|
|
21
|
+
acquireLock?: typeof acquireLock;
|
|
22
|
+
releaseLock?: typeof releaseLock;
|
|
23
|
+
}
|
|
24
|
+
export declare function ensureAssetStoreDirectory(path: string): void;
|
|
25
|
+
/** Validate an existing store root without recreating a deleted or unmounted path. */
|
|
26
|
+
export declare function assertAssetStoreDirectory(path: string): void;
|
|
27
|
+
export declare function isReliableAssetStoreLockRelease(result: ReleaseLockResult): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Run one synchronous asset-store critical section without hiding its primary failure.
|
|
30
|
+
* A release failure is surfaced only after a successful operation; when both fail, the operation error wins.
|
|
31
|
+
*/
|
|
32
|
+
export declare function withAssetStoreLock<T>(lockPath: string, operation: () => T, deps?: AssetStoreLockDeps): T;
|
|
33
|
+
export declare function assertOptionalRegularFile(path: string, role?: AssetStorePathRole): Stats | null;
|
|
34
|
+
export declare function regularFileFingerprint(path: string): string;
|
|
35
|
+
export declare function readRegularBuffer(path: string, maxBytes?: number): Buffer | null;
|
|
36
|
+
export declare function readUtf8Regular(path: string, maxBytes?: number): string | null;
|
|
37
|
+
export declare function createBufferDurableExclusive(path: string, value: Buffer, opts?: DurableWriteOptions): void;
|
|
38
|
+
export declare function fsyncDirectoryBestEffort(path: string): void;
|
|
39
|
+
export declare function appendUtf8Durable(path: string, value: string, opts?: DurableWriteOptions): void;
|
|
40
|
+
export declare function replaceUtf8Durable(path: string, value: string, opts?: DurableWriteOptions): void;
|
|
41
|
+
/** Remove one exact UTF-8 suffix and fsync the new file length; false means the suffix was not current. */
|
|
42
|
+
export declare function truncateUtf8SuffixDurable(path: string, suffix: string, opts?: Pick<DurableWriteOptions, 'syncFile'>): boolean;
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { closeSync, constants, fstatSync, fsyncSync, ftruncateSync, lstatSync, mkdirSync, openSync, readSync, renameSync, unlinkSync, writeSync, } from 'node:fs';
|
|
3
|
+
import { basename, dirname, join } from 'node:path';
|
|
4
|
+
import { acquireLock, LockReleaseError, releaseLock, } from '../util/fileLock.js';
|
|
5
|
+
export class UnsafeAssetStorePathError extends Error {
|
|
6
|
+
role;
|
|
7
|
+
reason;
|
|
8
|
+
code = 'UNSAFE_ASSET_STORE_PATH';
|
|
9
|
+
constructor(role, reason) {
|
|
10
|
+
super(`unsafe asset store ${role}: ${reason}`);
|
|
11
|
+
this.role = role;
|
|
12
|
+
this.reason = reason;
|
|
13
|
+
this.name = 'UnsafeAssetStorePathError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class AssetStoreReadLimitError extends Error {
|
|
17
|
+
code = 'ASSET_STORE_READ_LIMIT';
|
|
18
|
+
constructor() {
|
|
19
|
+
super('asset store file exceeds the configured read limit');
|
|
20
|
+
this.name = 'AssetStoreReadLimitError';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function isErrno(error, code) {
|
|
24
|
+
return typeof error === 'object' && error !== null && error.code === code;
|
|
25
|
+
}
|
|
26
|
+
function noFollowFlag() {
|
|
27
|
+
return constants['O_NOFOLLOW'] ?? 0;
|
|
28
|
+
}
|
|
29
|
+
function statOrNull(path) {
|
|
30
|
+
try {
|
|
31
|
+
return lstatSync(path);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (isErrno(error, 'ENOENT'))
|
|
35
|
+
return null;
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function assertRegularStat(stat, role) {
|
|
40
|
+
if (stat.isSymbolicLink())
|
|
41
|
+
throw new UnsafeAssetStorePathError(role, 'symlink');
|
|
42
|
+
if (!stat.isFile())
|
|
43
|
+
throw new UnsafeAssetStorePathError(role, 'not_regular_file');
|
|
44
|
+
return stat;
|
|
45
|
+
}
|
|
46
|
+
export function ensureAssetStoreDirectory(path) {
|
|
47
|
+
let stat = statOrNull(path);
|
|
48
|
+
if (stat === null) {
|
|
49
|
+
mkdirSync(path, { recursive: true, mode: 0o700 });
|
|
50
|
+
stat = statOrNull(path);
|
|
51
|
+
}
|
|
52
|
+
assertAssetStoreDirectoryStat(stat);
|
|
53
|
+
}
|
|
54
|
+
function assertAssetStoreDirectoryStat(stat) {
|
|
55
|
+
if (stat === null)
|
|
56
|
+
throw new UnsafeAssetStorePathError('base_directory', 'not_directory');
|
|
57
|
+
if (stat.isSymbolicLink())
|
|
58
|
+
throw new UnsafeAssetStorePathError('base_directory', 'symlink');
|
|
59
|
+
if (!stat.isDirectory())
|
|
60
|
+
throw new UnsafeAssetStorePathError('base_directory', 'not_directory');
|
|
61
|
+
}
|
|
62
|
+
/** Validate an existing store root without recreating a deleted or unmounted path. */
|
|
63
|
+
export function assertAssetStoreDirectory(path) {
|
|
64
|
+
assertAssetStoreDirectoryStat(statOrNull(path));
|
|
65
|
+
}
|
|
66
|
+
export function isReliableAssetStoreLockRelease(result) {
|
|
67
|
+
return result.released
|
|
68
|
+
&& (result.reason === 'released' || result.reason === 'released_with_cleanup_error');
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Run one synchronous asset-store critical section without hiding its primary failure.
|
|
72
|
+
* A release failure is surfaced only after a successful operation; when both fail, the operation error wins.
|
|
73
|
+
*/
|
|
74
|
+
export function withAssetStoreLock(lockPath, operation, deps = {}) {
|
|
75
|
+
assertOptionalRegularFile(lockPath, 'lock_file');
|
|
76
|
+
(deps.acquireLock ?? acquireLock)(lockPath);
|
|
77
|
+
let value;
|
|
78
|
+
let operationError;
|
|
79
|
+
let operationFailed = false;
|
|
80
|
+
try {
|
|
81
|
+
value = operation();
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
operationFailed = true;
|
|
85
|
+
operationError = error;
|
|
86
|
+
}
|
|
87
|
+
let releaseError;
|
|
88
|
+
try {
|
|
89
|
+
const released = (deps.releaseLock ?? releaseLock)(lockPath);
|
|
90
|
+
if (!isReliableAssetStoreLockRelease(released))
|
|
91
|
+
releaseError = new LockReleaseError(released.reason);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
releaseError = error;
|
|
95
|
+
}
|
|
96
|
+
if (operationFailed)
|
|
97
|
+
throw operationError;
|
|
98
|
+
if (releaseError !== undefined)
|
|
99
|
+
throw releaseError;
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
export function assertOptionalRegularFile(path, role = 'asset_file') {
|
|
103
|
+
const stat = statOrNull(path);
|
|
104
|
+
return stat === null ? null : assertRegularStat(stat, role);
|
|
105
|
+
}
|
|
106
|
+
export function regularFileFingerprint(path) {
|
|
107
|
+
try {
|
|
108
|
+
const stat = lstatSync(path, { bigint: true });
|
|
109
|
+
if (stat.isSymbolicLink())
|
|
110
|
+
throw new UnsafeAssetStorePathError('asset_file', 'symlink');
|
|
111
|
+
if (!stat.isFile())
|
|
112
|
+
throw new UnsafeAssetStorePathError('asset_file', 'not_regular_file');
|
|
113
|
+
return `${stat.dev}:${stat.ino}:${stat.mode}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (isErrno(error, 'ENOENT'))
|
|
117
|
+
return 'missing';
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function assertOpenedPathMatches(fd, path, role) {
|
|
122
|
+
const opened = fstatSync(fd, { bigint: true });
|
|
123
|
+
if (!opened.isFile())
|
|
124
|
+
throw new UnsafeAssetStorePathError(role, 'not_regular_file');
|
|
125
|
+
let current;
|
|
126
|
+
try {
|
|
127
|
+
current = lstatSync(path, { bigint: true });
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
if (isErrno(error, 'ENOENT'))
|
|
131
|
+
throw new UnsafeAssetStorePathError(role, 'path_changed');
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
if (current.isSymbolicLink())
|
|
135
|
+
throw new UnsafeAssetStorePathError(role, 'symlink');
|
|
136
|
+
if (!current.isFile())
|
|
137
|
+
throw new UnsafeAssetStorePathError(role, 'not_regular_file');
|
|
138
|
+
if (opened.dev !== current.dev || opened.ino !== current.ino) {
|
|
139
|
+
throw new UnsafeAssetStorePathError(role, 'path_changed');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function openNoFollow(path, flags, mode) {
|
|
143
|
+
return mode === undefined
|
|
144
|
+
? openSync(path, flags | noFollowFlag())
|
|
145
|
+
: openSync(path, flags | noFollowFlag(), mode);
|
|
146
|
+
}
|
|
147
|
+
export function readRegularBuffer(path, maxBytes = Number.MAX_SAFE_INTEGER) {
|
|
148
|
+
if (assertOptionalRegularFile(path) === null)
|
|
149
|
+
return null;
|
|
150
|
+
const fd = openNoFollow(path, constants.O_RDONLY);
|
|
151
|
+
try {
|
|
152
|
+
assertOpenedPathMatches(fd, path, 'asset_file');
|
|
153
|
+
if (fstatSync(fd).size > maxBytes)
|
|
154
|
+
throw new AssetStoreReadLimitError();
|
|
155
|
+
const chunks = [];
|
|
156
|
+
let total = 0;
|
|
157
|
+
while (total <= maxBytes) {
|
|
158
|
+
const chunk = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes - total + 1));
|
|
159
|
+
const bytesRead = readSync(fd, chunk, 0, chunk.byteLength, null);
|
|
160
|
+
if (bytesRead === 0)
|
|
161
|
+
break;
|
|
162
|
+
total += bytesRead;
|
|
163
|
+
if (total > maxBytes)
|
|
164
|
+
throw new AssetStoreReadLimitError();
|
|
165
|
+
chunks.push(bytesRead === chunk.byteLength ? chunk : chunk.subarray(0, bytesRead));
|
|
166
|
+
}
|
|
167
|
+
return Buffer.concat(chunks, total);
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
closeSync(fd);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export function readUtf8Regular(path, maxBytes = Number.MAX_SAFE_INTEGER) {
|
|
174
|
+
return readRegularBuffer(path, maxBytes)?.toString('utf8') ?? null;
|
|
175
|
+
}
|
|
176
|
+
function writeAll(fd, value) {
|
|
177
|
+
const bytes = typeof value === 'string' ? Buffer.from(value, 'utf8') : value;
|
|
178
|
+
let offset = 0;
|
|
179
|
+
while (offset < bytes.length) {
|
|
180
|
+
const written = writeSync(fd, bytes, offset, bytes.length - offset);
|
|
181
|
+
if (written <= 0)
|
|
182
|
+
throw new Error('asset store write made no progress');
|
|
183
|
+
offset += written;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
export function createBufferDurableExclusive(path, value, opts = {}) {
|
|
187
|
+
const parent = dirname(path);
|
|
188
|
+
assertAssetStoreDirectory(parent);
|
|
189
|
+
const fd = openNoFollow(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600);
|
|
190
|
+
let operationError;
|
|
191
|
+
let openedIdentity;
|
|
192
|
+
try {
|
|
193
|
+
assertOpenedPathMatches(fd, path, 'asset_file');
|
|
194
|
+
const opened = fstatSync(fd, { bigint: true });
|
|
195
|
+
openedIdentity = { dev: opened.dev, ino: opened.ino };
|
|
196
|
+
writeAll(fd, value);
|
|
197
|
+
(opts.syncFile ?? fsyncSync)(fd);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
operationError = error;
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
try {
|
|
204
|
+
closeSync(fd);
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
if (operationError === undefined)
|
|
208
|
+
operationError = error;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (operationError !== undefined) {
|
|
212
|
+
try {
|
|
213
|
+
const current = lstatSync(path, { bigint: true });
|
|
214
|
+
if (openedIdentity
|
|
215
|
+
&& !current.isSymbolicLink()
|
|
216
|
+
&& current.isFile()
|
|
217
|
+
&& current.dev === openedIdentity.dev
|
|
218
|
+
&& current.ino === openedIdentity.ino) {
|
|
219
|
+
unlinkSync(path);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch { /* preserve the primary write/fsync failure */ }
|
|
223
|
+
throw operationError;
|
|
224
|
+
}
|
|
225
|
+
(opts.syncDirectory ?? fsyncDirectoryBestEffort)(parent);
|
|
226
|
+
}
|
|
227
|
+
export function fsyncDirectoryBestEffort(path) {
|
|
228
|
+
let fd;
|
|
229
|
+
try {
|
|
230
|
+
fd = openSync(path, constants.O_RDONLY);
|
|
231
|
+
fsyncSync(fd);
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
// Windows and some filesystems do not support syncing directory handles.
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
if (fd !== undefined) {
|
|
238
|
+
try {
|
|
239
|
+
closeSync(fd);
|
|
240
|
+
}
|
|
241
|
+
catch { /* best-effort directory sync must stay best-effort */ }
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
export function appendUtf8Durable(path, value, opts = {}) {
|
|
246
|
+
const parent = dirname(path);
|
|
247
|
+
assertAssetStoreDirectory(parent);
|
|
248
|
+
const existed = assertOptionalRegularFile(path) !== null;
|
|
249
|
+
const fd = openNoFollow(path, constants.O_RDWR | constants.O_APPEND | constants.O_CREAT, 0o600);
|
|
250
|
+
try {
|
|
251
|
+
assertOpenedPathMatches(fd, path, 'asset_file');
|
|
252
|
+
const stat = fstatSync(fd);
|
|
253
|
+
let needsLineSeparator = false;
|
|
254
|
+
if (stat.size > 0 && value.length > 0) {
|
|
255
|
+
const tail = Buffer.allocUnsafe(1);
|
|
256
|
+
const bytesRead = readSync(fd, tail, 0, 1, stat.size - 1);
|
|
257
|
+
if (bytesRead !== 1)
|
|
258
|
+
throw new Error('asset store tail read made no progress');
|
|
259
|
+
needsLineSeparator = tail[0] !== 0x0a;
|
|
260
|
+
}
|
|
261
|
+
writeAll(fd, needsLineSeparator ? `\n${value}` : value);
|
|
262
|
+
(opts.syncFile ?? fsyncSync)(fd);
|
|
263
|
+
}
|
|
264
|
+
finally {
|
|
265
|
+
closeSync(fd);
|
|
266
|
+
}
|
|
267
|
+
if (!existed)
|
|
268
|
+
(opts.syncDirectory ?? fsyncDirectoryBestEffort)(parent);
|
|
269
|
+
}
|
|
270
|
+
function removeTemp(path) {
|
|
271
|
+
try {
|
|
272
|
+
unlinkSync(path);
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
if (!isErrno(error, 'ENOENT'))
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
export function replaceUtf8Durable(path, value, opts = {}) {
|
|
280
|
+
const parent = dirname(path);
|
|
281
|
+
assertAssetStoreDirectory(parent);
|
|
282
|
+
assertOptionalRegularFile(path);
|
|
283
|
+
const tempPath = join(parent, `.${basename(path)}.compact.${process.pid}.${randomUUID()}.tmp`);
|
|
284
|
+
opts.onTempPath?.(tempPath);
|
|
285
|
+
let renamed = false;
|
|
286
|
+
try {
|
|
287
|
+
const fd = openNoFollow(tempPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o600);
|
|
288
|
+
try {
|
|
289
|
+
assertOpenedPathMatches(fd, tempPath, 'temp_file');
|
|
290
|
+
writeAll(fd, value);
|
|
291
|
+
(opts.syncFile ?? fsyncSync)(fd);
|
|
292
|
+
}
|
|
293
|
+
finally {
|
|
294
|
+
closeSync(fd);
|
|
295
|
+
}
|
|
296
|
+
assertOptionalRegularFile(path);
|
|
297
|
+
renameSync(tempPath, path);
|
|
298
|
+
renamed = true;
|
|
299
|
+
(opts.syncDirectory ?? fsyncDirectoryBestEffort)(parent);
|
|
300
|
+
}
|
|
301
|
+
finally {
|
|
302
|
+
if (!renamed)
|
|
303
|
+
removeTemp(tempPath);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/** Remove one exact UTF-8 suffix and fsync the new file length; false means the suffix was not current. */
|
|
307
|
+
export function truncateUtf8SuffixDurable(path, suffix, opts = {}) {
|
|
308
|
+
if (assertOptionalRegularFile(path) === null)
|
|
309
|
+
return false;
|
|
310
|
+
const expected = Buffer.from(suffix, 'utf8');
|
|
311
|
+
if (expected.length === 0)
|
|
312
|
+
return false;
|
|
313
|
+
const fd = openNoFollow(path, constants.O_RDWR);
|
|
314
|
+
try {
|
|
315
|
+
assertOpenedPathMatches(fd, path, 'asset_file');
|
|
316
|
+
const stat = fstatSync(fd);
|
|
317
|
+
if (stat.size < expected.length)
|
|
318
|
+
return false;
|
|
319
|
+
const actual = Buffer.alloc(expected.length);
|
|
320
|
+
let offset = 0;
|
|
321
|
+
while (offset < actual.length) {
|
|
322
|
+
const read = readSync(fd, actual, offset, actual.length - offset, stat.size - actual.length + offset);
|
|
323
|
+
if (read === 0)
|
|
324
|
+
return false;
|
|
325
|
+
offset += read;
|
|
326
|
+
}
|
|
327
|
+
if (!actual.equals(expected))
|
|
328
|
+
return false;
|
|
329
|
+
ftruncateSync(fd, stat.size - expected.length);
|
|
330
|
+
(opts.syncFile ?? fsyncSync)(fd);
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
finally {
|
|
334
|
+
closeSync(fd);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { AssetKind } from './provider.js';
|
|
2
|
+
export declare const ASSET_SYNC_INVENTORY_MAX_SEGMENTS = 24;
|
|
3
|
+
export declare const ASSET_SYNC_INVENTORY_MAX_ITEMS_PER_SEGMENT = 10000;
|
|
4
|
+
export declare const ASSET_SYNC_INVENTORY_MAX_UNIQUE_ITEMS = 240000;
|
|
5
|
+
export declare const ASSET_SYNC_INVENTORY_MAX_TOTAL_BYTES: number;
|
|
2
6
|
export type AssetSyncSource = 'hub';
|
|
3
7
|
export type AssetSyncScope = 'purchased' | 'published';
|
|
4
8
|
export interface AssetSyncRecord {
|
|
@@ -8,21 +12,107 @@ export interface AssetSyncRecord {
|
|
|
8
12
|
scope: AssetSyncScope;
|
|
9
13
|
syncedAt: string;
|
|
10
14
|
remoteAssetId: string;
|
|
15
|
+
/** Opaque sync identity hash. Legacy records omit it and are not used for identity-scoped reconciliation. */
|
|
16
|
+
runKey?: string;
|
|
17
|
+
/** Parameter-independent remote inventory identity used for reconciliation across sync runs. */
|
|
18
|
+
inventoryKey?: string;
|
|
11
19
|
logicalId?: string;
|
|
12
20
|
status?: string;
|
|
13
21
|
forced?: true;
|
|
14
22
|
collisionWithAssetId?: string;
|
|
15
23
|
}
|
|
24
|
+
export type AssetSyncRunState = 'started' | 'progress' | 'completed';
|
|
25
|
+
export type AssetSyncRunOutcome = 'imported' | 'already_local' | 'failed' | 'remote_missing';
|
|
26
|
+
export interface AssetSyncRunRecord {
|
|
27
|
+
recordType: 'run';
|
|
28
|
+
runId: string;
|
|
29
|
+
runKey: string;
|
|
30
|
+
state: AssetSyncRunState;
|
|
31
|
+
remoteAssetId?: string;
|
|
32
|
+
outcome?: AssetSyncRunOutcome;
|
|
33
|
+
reason?: string;
|
|
34
|
+
/** Ordered candidate IDs selected when this run was started. */
|
|
35
|
+
plan?: readonly string[];
|
|
36
|
+
syncedAt: string;
|
|
37
|
+
}
|
|
38
|
+
export interface AssetSyncRunSnapshot extends AssetSyncRunRecord {
|
|
39
|
+
readonly processed: ReadonlyMap<string, AssetSyncRunRecord>;
|
|
40
|
+
}
|
|
41
|
+
export type AssetSyncInventoryOutcome = 'imported' | 'already_local' | 'blocked' | 'failed' | 'pending';
|
|
42
|
+
export interface AssetSyncInventoryCursorFingerprints {
|
|
43
|
+
readonly purchased: string | null;
|
|
44
|
+
readonly published: string | null;
|
|
45
|
+
}
|
|
46
|
+
export interface AssetSyncInventoryItem {
|
|
47
|
+
readonly remoteAssetId: string;
|
|
48
|
+
readonly outcome: AssetSyncInventoryOutcome;
|
|
49
|
+
}
|
|
50
|
+
export interface AssetSyncInventorySegmentRecord {
|
|
51
|
+
readonly recordType: 'inventory_scan';
|
|
52
|
+
readonly scanId: string;
|
|
53
|
+
readonly inventoryKey: string;
|
|
54
|
+
readonly scope: 'purchased' | 'published' | 'all';
|
|
55
|
+
readonly index: number;
|
|
56
|
+
readonly inputCursorFingerprints: AssetSyncInventoryCursorFingerprints;
|
|
57
|
+
readonly nextCursorFingerprints: AssetSyncInventoryCursorFingerprints;
|
|
58
|
+
readonly items: readonly AssetSyncInventoryItem[];
|
|
59
|
+
readonly anonymousBlocked: number;
|
|
60
|
+
/** This segment must be replayed from its input cursor before the scan may advance. */
|
|
61
|
+
readonly cursorHeld?: true;
|
|
62
|
+
/** Multi-segment batches are applied only after every physical segment is present. */
|
|
63
|
+
readonly batchId?: string;
|
|
64
|
+
readonly batchIndex?: number;
|
|
65
|
+
readonly batchSize?: number;
|
|
66
|
+
readonly syncedAt: string;
|
|
67
|
+
}
|
|
68
|
+
export type AssetSyncInventoryBatch = Omit<AssetSyncInventorySegmentRecord, 'recordType' | 'syncedAt' | 'batchId' | 'batchIndex' | 'batchSize'> & {
|
|
69
|
+
syncedAt?: string;
|
|
70
|
+
};
|
|
71
|
+
export interface AssetSyncInventorySnapshot {
|
|
72
|
+
readonly scanId: string;
|
|
73
|
+
readonly inventoryKey: string;
|
|
74
|
+
readonly scope: 'purchased' | 'published' | 'all';
|
|
75
|
+
readonly segmentCount: number;
|
|
76
|
+
readonly nextCursorFingerprints: AssetSyncInventoryCursorFingerprints;
|
|
77
|
+
readonly outcomes: ReadonlyMap<string, AssetSyncInventoryOutcome>;
|
|
78
|
+
readonly anonymousBlocked: number;
|
|
79
|
+
readonly complete: boolean;
|
|
80
|
+
/** Physical segment index whose input cursor must be replayed before this scan can advance. */
|
|
81
|
+
readonly retryIndex?: number;
|
|
82
|
+
}
|
|
16
83
|
export declare class AssetSyncLedger {
|
|
17
84
|
private readonly now;
|
|
18
85
|
private readonly path;
|
|
86
|
+
private readonly lockPath;
|
|
19
87
|
private readonly index;
|
|
88
|
+
private readonly runKeyIndex;
|
|
89
|
+
private readonly inventoryKeyIndex;
|
|
90
|
+
private fileState;
|
|
20
91
|
private loaded;
|
|
21
92
|
constructor(baseDir: string, now?: () => number);
|
|
22
93
|
append(rec: Omit<AssetSyncRecord, 'syncedAt'> & {
|
|
23
94
|
syncedAt?: string;
|
|
24
95
|
}): AssetSyncRecord;
|
|
96
|
+
appendRun(rec: Omit<AssetSyncRunRecord, 'recordType' | 'syncedAt'> & {
|
|
97
|
+
syncedAt?: string;
|
|
98
|
+
}): AssetSyncRunRecord;
|
|
99
|
+
appendInventorySegment(rec: Omit<AssetSyncInventorySegmentRecord, 'recordType' | 'syncedAt'> & {
|
|
100
|
+
syncedAt?: string;
|
|
101
|
+
}): AssetSyncInventorySegmentRecord | null;
|
|
102
|
+
appendInventoryBatch(rec: AssetSyncInventoryBatch): readonly AssetSyncInventorySegmentRecord[] | null;
|
|
103
|
+
replaceInventoryRetryBatch(rec: AssetSyncInventoryBatch): readonly AssetSyncInventorySegmentRecord[] | null;
|
|
104
|
+
clearInventoryScan(inventoryKey: string): void;
|
|
105
|
+
runRecords(runId: string): AssetSyncRunRecord[];
|
|
106
|
+
latestIncompleteRun(runKey: string): AssetSyncRunSnapshot | undefined;
|
|
107
|
+
latestInventoryScan(inventoryKey: string): AssetSyncInventorySnapshot | undefined;
|
|
25
108
|
get(assetId: string): AssetSyncRecord | null;
|
|
109
|
+
getForRunKey(runKey: string, assetId: string): AssetSyncRecord | null;
|
|
26
110
|
list(): AssetSyncRecord[];
|
|
27
|
-
|
|
111
|
+
listForRunKey(runKey: string): AssetSyncRecord[];
|
|
112
|
+
listForInventoryKey(inventoryKey: string): AssetSyncRecord[];
|
|
113
|
+
private rebuildIndex;
|
|
114
|
+
private refreshUnderLock;
|
|
115
|
+
private withFreshRead;
|
|
116
|
+
private readRunRecordsUnderLock;
|
|
117
|
+
private latestInventorySnapshotUnderLock;
|
|
28
118
|
}
|