swarm-deploy 0.0.0 → 0.2.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/CHANGELOG.md +76 -0
- package/LICENSE.md +201 -0
- package/NOTICE.md +13 -0
- package/README.md +1283 -0
- package/RELEASING.md +71 -0
- package/SECURITY.md +40 -0
- package/dist/abort.d.ts +16 -0
- package/dist/abort.js +52 -0
- package/dist/abort.js.map +1 -0
- package/dist/allowlist.d.ts +2 -0
- package/dist/allowlist.js +21 -0
- package/dist/allowlist.js.map +1 -0
- package/dist/bin/swarm-deploy.d.ts +2 -0
- package/dist/bin/swarm-deploy.js +41 -0
- package/dist/bin/swarm-deploy.js.map +1 -0
- package/dist/cli.d.ts +27 -0
- package/dist/cli.js +751 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +111 -0
- package/dist/client.js +276 -0
- package/dist/client.js.map +1 -0
- package/dist/direct-dht.d.ts +81 -0
- package/dist/direct-dht.js +254 -0
- package/dist/direct-dht.js.map +1 -0
- package/dist/error-code.d.ts +15 -0
- package/dist/error-code.js +30 -0
- package/dist/error-code.js.map +1 -0
- package/dist/errors.d.ts +31 -0
- package/dist/errors.js +37 -0
- package/dist/errors.js.map +1 -0
- package/dist/events-node.d.ts +2 -0
- package/dist/events-node.js +7 -0
- package/dist/events-node.js.map +1 -0
- package/dist/files.d.ts +32 -0
- package/dist/files.js +85 -0
- package/dist/files.js.map +1 -0
- package/dist/fs-node.d.ts +2 -0
- package/dist/fs-node.js +7 -0
- package/dist/fs-node.js.map +1 -0
- package/dist/hooks-module.d.ts +9 -0
- package/dist/hooks-module.js +118 -0
- package/dist/hooks-module.js.map +1 -0
- package/dist/hooks.d.ts +69 -0
- package/dist/hooks.js +88 -0
- package/dist/hooks.js.map +1 -0
- package/dist/identity.d.ts +7 -0
- package/dist/identity.js +50 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/os-node.d.ts +2 -0
- package/dist/os-node.js +7 -0
- package/dist/os-node.js.map +1 -0
- package/dist/path-node.d.ts +2 -0
- package/dist/path-node.js +7 -0
- package/dist/path-node.js.map +1 -0
- package/dist/process-node.d.ts +2 -0
- package/dist/process-node.js +7 -0
- package/dist/process-node.js.map +1 -0
- package/dist/release.d.ts +26 -0
- package/dist/release.js +419 -0
- package/dist/release.js.map +1 -0
- package/dist/server.d.ts +239 -0
- package/dist/server.js +928 -0
- package/dist/server.js.map +1 -0
- package/dist/storage/atomic-file.d.ts +15 -0
- package/dist/storage/atomic-file.js +172 -0
- package/dist/storage/atomic-file.js.map +1 -0
- package/dist/storage/commit-journal.d.ts +120 -0
- package/dist/storage/commit-journal.js +417 -0
- package/dist/storage/commit-journal.js.map +1 -0
- package/dist/storage/commit-store.d.ts +307 -0
- package/dist/storage/commit-store.js +1691 -0
- package/dist/storage/commit-store.js.map +1 -0
- package/dist/storage/layout.d.ts +16 -0
- package/dist/storage/layout.js +409 -0
- package/dist/storage/layout.js.map +1 -0
- package/dist/storage/link-store.d.ts +70 -0
- package/dist/storage/link-store.js +363 -0
- package/dist/storage/link-store.js.map +1 -0
- package/dist/storage/recovery.d.ts +100 -0
- package/dist/storage/recovery.js +260 -0
- package/dist/storage/recovery.js.map +1 -0
- package/dist/storage/retention.d.ts +183 -0
- package/dist/storage/retention.js +619 -0
- package/dist/storage/retention.js.map +1 -0
- package/dist/storage/root-coordinator.d.ts +7 -0
- package/dist/storage/root-coordinator.js +27 -0
- package/dist/storage/root-coordinator.js.map +1 -0
- package/dist/storage/session-store.d.ts +2 -0
- package/dist/storage/session-store.js +7 -0
- package/dist/storage/session-store.js.map +1 -0
- package/dist/storage/tar-session-store.d.ts +147 -0
- package/dist/storage/tar-session-store.js +1044 -0
- package/dist/storage/tar-session-store.js.map +1 -0
- package/dist/storage/tree-fs.d.ts +44 -0
- package/dist/storage/tree-fs.js +580 -0
- package/dist/storage/tree-fs.js.map +1 -0
- package/dist/storage/tree-staging.d.ts +9 -0
- package/dist/storage/tree-staging.js +64 -0
- package/dist/storage/tree-staging.js.map +1 -0
- package/dist/storage/types.d.ts +77 -0
- package/dist/storage/types.js +3 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/symlinks.d.ts +36 -0
- package/dist/symlinks.js +128 -0
- package/dist/symlinks.js.map +1 -0
- package/dist/tar-protocol/controls.d.ts +73 -0
- package/dist/tar-protocol/controls.js +291 -0
- package/dist/tar-protocol/controls.js.map +1 -0
- package/dist/tar-protocol/direct-wire.d.ts +43 -0
- package/dist/tar-protocol/direct-wire.js +194 -0
- package/dist/tar-protocol/direct-wire.js.map +1 -0
- package/dist/tar-protocol/extract.d.ts +18 -0
- package/dist/tar-protocol/extract.js +124 -0
- package/dist/tar-protocol/extract.js.map +1 -0
- package/dist/tar-protocol/hash.d.ts +10 -0
- package/dist/tar-protocol/hash.js +44 -0
- package/dist/tar-protocol/hash.js.map +1 -0
- package/dist/tar-protocol/lifecycle.d.ts +13 -0
- package/dist/tar-protocol/lifecycle.js +90 -0
- package/dist/tar-protocol/lifecycle.js.map +1 -0
- package/dist/tar-protocol/manifest.d.ts +58 -0
- package/dist/tar-protocol/manifest.js +312 -0
- package/dist/tar-protocol/manifest.js.map +1 -0
- package/dist/tar-protocol/tree-extract.d.ts +37 -0
- package/dist/tar-protocol/tree-extract.js +240 -0
- package/dist/tar-protocol/tree-extract.js.map +1 -0
- package/dist/tar-protocol/tree-manifest.d.ts +45 -0
- package/dist/tar-protocol/tree-manifest.js +277 -0
- package/dist/tar-protocol/tree-manifest.js.map +1 -0
- package/dist/tar-protocol/tree-path.d.ts +15 -0
- package/dist/tar-protocol/tree-path.js +68 -0
- package/dist/tar-protocol/tree-path.js.map +1 -0
- package/dist/tar-protocol/tree.d.ts +68 -0
- package/dist/tar-protocol/tree.js +269 -0
- package/dist/tar-protocol/tree.js.map +1 -0
- package/dist/tar-protocol/ustar.d.ts +36 -0
- package/dist/tar-protocol/ustar.js +167 -0
- package/dist/tar-protocol/ustar.js.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +4 -0
- package/dist/validation.js +26 -0
- package/dist/validation.js.map +1 -0
- package/package.json +103 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTreeStagingTarget = createTreeStagingTarget;
|
|
4
|
+
const errors_js_1 = require("../errors.js");
|
|
5
|
+
const tree_fs_js_1 = require("./tree-fs.js");
|
|
6
|
+
/**
|
|
7
|
+
* Writes an extracted tree into `treePath`, which must live directly under
|
|
8
|
+
* `parent`. Every path is created exclusively, with no-follow opens, from
|
|
9
|
+
* validated components only, and every call carries the storage layout so the
|
|
10
|
+
* protected directories are refused by identity.
|
|
11
|
+
*/
|
|
12
|
+
function createTreeStagingTarget(treePath, parent, storage, layout) {
|
|
13
|
+
const options = { layout };
|
|
14
|
+
let created = false;
|
|
15
|
+
const ensureRoot = async () => {
|
|
16
|
+
if (created)
|
|
17
|
+
return;
|
|
18
|
+
await (0, tree_fs_js_1.createTreeRoot)(treePath, parent, storage, options);
|
|
19
|
+
created = true;
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
async createDirectory(relativePath) {
|
|
23
|
+
await ensureRoot();
|
|
24
|
+
await (0, tree_fs_js_1.createTreeSubdirectory)(treePath, relativePath, storage, options);
|
|
25
|
+
},
|
|
26
|
+
async createFile(relativePath) {
|
|
27
|
+
await ensureRoot();
|
|
28
|
+
const handle = await (0, tree_fs_js_1.openTreeFile)(treePath, relativePath, storage, options);
|
|
29
|
+
let position = 0;
|
|
30
|
+
return {
|
|
31
|
+
async write(chunk) {
|
|
32
|
+
let offset = 0;
|
|
33
|
+
while (offset < chunk.byteLength) {
|
|
34
|
+
const written = await handle.write(chunk, offset, chunk.byteLength - offset, position + offset);
|
|
35
|
+
const count = typeof written === 'number' ? written : written.bytesWritten;
|
|
36
|
+
if (!Number.isSafeInteger(count) || count <= 0) {
|
|
37
|
+
throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.PROTOCOL_INVALID, 'Unable to write staged tree file');
|
|
38
|
+
}
|
|
39
|
+
offset += count;
|
|
40
|
+
}
|
|
41
|
+
position += chunk.byteLength;
|
|
42
|
+
},
|
|
43
|
+
async close() {
|
|
44
|
+
try {
|
|
45
|
+
await handle.sync();
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
await handle.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
async complete() {
|
|
54
|
+
await ensureRoot();
|
|
55
|
+
await (0, tree_fs_js_1.syncTreeDirectories)(treePath, storage);
|
|
56
|
+
},
|
|
57
|
+
async abort() {
|
|
58
|
+
if (!created)
|
|
59
|
+
return;
|
|
60
|
+
await (0, tree_fs_js_1.removeTree)(treePath, parent, storage, options).catch(() => { });
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=tree-staging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-staging.js","sourceRoot":"","sources":["../../src/storage/tree-staging.ts"],"names":[],"mappings":";;;AAAA,4CAAuD;AAEvD,6CAMqB;AAGrB;;;;;GAKG;AACH,iCACE,QAAgB,EAChB,MAAc,EACd,OAAuB,EACvB,MAAqB;IAErB,MAAM,OAAO,GAAG,EAAE,MAAM,EAAE,CAAA;IAC1B,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,MAAM,UAAU,GAAG,KAAK,IAAmB,EAAE;QAC3C,IAAI,OAAO;YAAE,OAAM;QACnB,MAAM,IAAA,2BAAc,EAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;QACxD,OAAO,GAAG,IAAI,CAAA;IAChB,CAAC,CAAA;IACD,OAAO;QACL,KAAK,CAAC,eAAe,CAAC,YAAoB;YACxC,MAAM,UAAU,EAAE,CAAA;YAClB,MAAM,IAAA,mCAAsB,EAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;QACxE,CAAC;QACD,KAAK,CAAC,UAAU,CAAC,YAAoB;YACnC,MAAM,UAAU,EAAE,CAAA;YAClB,MAAM,MAAM,GAAG,MAAM,IAAA,yBAAY,EAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;YAC3E,IAAI,QAAQ,GAAG,CAAC,CAAA;YAChB,OAAO;gBACL,KAAK,CAAC,KAAK,CAAC,KAAiB;oBAC3B,IAAI,MAAM,GAAG,CAAC,CAAA;oBACd,OAAO,MAAM,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC;wBACjC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,KAAK,CAChC,KAAK,EACL,MAAM,EACN,KAAK,CAAC,UAAU,GAAG,MAAM,EACzB,QAAQ,GAAG,MAAM,CAClB,CAAA;wBACD,MAAM,KAAK,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAA;wBAC1E,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;4BAC/C,MAAM,IAAI,4BAAgB,CACxB,kBAAM,CAAC,gBAAgB,EACvB,kCAAkC,CACnC,CAAA;wBACH,CAAC;wBACD,MAAM,IAAI,KAAK,CAAA;oBACjB,CAAC;oBACD,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAA;gBAC9B,CAAC;gBACD,KAAK,CAAC,KAAK;oBACT,IAAI,CAAC;wBACH,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;oBACrB,CAAC;4BAAS,CAAC;wBACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;oBACtB,CAAC;gBACH,CAAC;aACF,CAAA;QACH,CAAC;QACD,KAAK,CAAC,QAAQ;YACZ,MAAM,UAAU,EAAE,CAAA;YAClB,MAAM,IAAA,gCAAmB,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QAC9C,CAAC;QACD,KAAK,CAAC,KAAK;YACT,IAAI,CAAC,OAAO;gBAAE,OAAM;YACpB,MAAM,IAAA,uBAAU,EAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QACtE,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export interface StorageStats {
|
|
2
|
+
dev: number | bigint;
|
|
3
|
+
ino: number | bigint;
|
|
4
|
+
size: number;
|
|
5
|
+
/** Hard-link count; tree primitives reject any file reporting more than one. */
|
|
6
|
+
nlink?: number;
|
|
7
|
+
/** Modification time, compared across hashing when the adapter reports it. */
|
|
8
|
+
mtimeMs?: number;
|
|
9
|
+
isSymbolicLink(): boolean;
|
|
10
|
+
isDirectory(): boolean;
|
|
11
|
+
isFile(): boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Historical internal alias for {@link StorageStats}. */
|
|
14
|
+
export type StorageStat = StorageStats;
|
|
15
|
+
export interface StorageReadResult {
|
|
16
|
+
bytesRead: number;
|
|
17
|
+
}
|
|
18
|
+
export interface StorageWriteResult {
|
|
19
|
+
bytesWritten: number;
|
|
20
|
+
}
|
|
21
|
+
export interface StorageMkdirOptions {
|
|
22
|
+
mode?: number;
|
|
23
|
+
}
|
|
24
|
+
export interface StorageRmOptions {
|
|
25
|
+
recursive: boolean;
|
|
26
|
+
force: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface StorageStatFs {
|
|
29
|
+
bavail: number | bigint;
|
|
30
|
+
bsize: number | bigint;
|
|
31
|
+
}
|
|
32
|
+
export interface StorageFileHandle {
|
|
33
|
+
stat(): Promise<StorageStats>;
|
|
34
|
+
write(bytes: Uint8Array, offset: number, length: number, position: number | null): Promise<number | StorageWriteResult>;
|
|
35
|
+
read(bytes: Uint8Array, offset: number, length: number, position: number | null): Promise<number | StorageReadResult>;
|
|
36
|
+
sync(): Promise<void>;
|
|
37
|
+
close(): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export interface StorageAdapter {
|
|
40
|
+
readFile(path: string, encoding: 'utf8'): Promise<string | Uint8Array>;
|
|
41
|
+
lstat(path: string): Promise<StorageStats>;
|
|
42
|
+
open(path: string, flags: string | number, mode?: number): Promise<StorageFileHandle>;
|
|
43
|
+
mkdir(path: string, options?: StorageMkdirOptions): Promise<void>;
|
|
44
|
+
rm(path: string, options?: Partial<StorageRmOptions>): Promise<void>;
|
|
45
|
+
rename(source: string, destination: string): Promise<void>;
|
|
46
|
+
link(existingPath: string, newPath: string): Promise<void>;
|
|
47
|
+
unlink(path: string): Promise<void>;
|
|
48
|
+
rmdir(path: string): Promise<void>;
|
|
49
|
+
readdir(path: string): Promise<string[]>;
|
|
50
|
+
statfs?(path: string): Promise<StorageStatFs>;
|
|
51
|
+
/** Required only when symlink rules are configured. */
|
|
52
|
+
symlink?(target: string, path: string): Promise<void>;
|
|
53
|
+
readlink?(path: string): Promise<string>;
|
|
54
|
+
}
|
|
55
|
+
export type SymlinkCapableStorage = StorageAdapter & {
|
|
56
|
+
symlink(target: string, path: string): Promise<void>;
|
|
57
|
+
readlink(path: string): Promise<string>;
|
|
58
|
+
};
|
|
59
|
+
export interface StorageLayout {
|
|
60
|
+
root: string;
|
|
61
|
+
internal: string;
|
|
62
|
+
staging: string;
|
|
63
|
+
sessions: string;
|
|
64
|
+
commits: string;
|
|
65
|
+
journals: string;
|
|
66
|
+
/** Private destinations for replacement publications before they are visible. */
|
|
67
|
+
publications: string;
|
|
68
|
+
/** Private managed-symlink ownership records. */
|
|
69
|
+
links: string;
|
|
70
|
+
/** Private destination a managed directory is renamed into before deletion. */
|
|
71
|
+
trash: string;
|
|
72
|
+
lock: string;
|
|
73
|
+
}
|
|
74
|
+
export interface StorageIdentity {
|
|
75
|
+
dev: number | bigint;
|
|
76
|
+
ino: number | bigint;
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type CommitRecord } from './storage/commit-journal.js';
|
|
2
|
+
import type { ArtifactKind } from './types.js';
|
|
3
|
+
export declare const MAX_SYMLINK_SELECTOR_BYTES = 200;
|
|
4
|
+
export interface SymlinkRule {
|
|
5
|
+
/** An exact managed artifact basename, or `/pattern/` with no flags. */
|
|
6
|
+
selector: string;
|
|
7
|
+
/** The safe basename of the managed link, a sibling of its target. */
|
|
8
|
+
name: string;
|
|
9
|
+
}
|
|
10
|
+
export interface CompiledSymlinkRule {
|
|
11
|
+
readonly selector: string;
|
|
12
|
+
readonly name: string;
|
|
13
|
+
/** The exact target basename, or `null` for a regular-expression selector. */
|
|
14
|
+
readonly exact: string | null;
|
|
15
|
+
matches(name: string): boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface DesiredLink {
|
|
18
|
+
name: string;
|
|
19
|
+
/** Always exactly the selected record's basename. */
|
|
20
|
+
target: string;
|
|
21
|
+
transferId: string;
|
|
22
|
+
targetKind: ArtifactKind;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Validates and snapshots operator symlink rules. The regex source is bounded,
|
|
26
|
+
* has no flags, and runs only against validated 100-byte artifact basenames; it
|
|
27
|
+
* is trusted operator configuration, and an invalid one is a startup error.
|
|
28
|
+
*/
|
|
29
|
+
export declare function compileSymlinkRules(rules: Iterable<SymlinkRule> | null | undefined): readonly CompiledSymlinkRule[];
|
|
30
|
+
export declare function symlinkRuleNames(rules: readonly CompiledSymlinkRule[]): ReadonlySet<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Computes the desired links from validated commit records only. A rule with no
|
|
33
|
+
* match is dormant. Replacement history names never match, and a candidate
|
|
34
|
+
* whose name equals its own link name is skipped.
|
|
35
|
+
*/
|
|
36
|
+
export declare function selectDesiredLinks(rules: readonly CompiledSymlinkRule[], records: readonly CommitRecord[]): DesiredLink[];
|
package/dist/symlinks.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MAX_SYMLINK_SELECTOR_BYTES = void 0;
|
|
7
|
+
exports.compileSymlinkRules = compileSymlinkRules;
|
|
8
|
+
exports.symlinkRuleNames = symlinkRuleNames;
|
|
9
|
+
exports.selectDesiredLinks = selectDesiredLinks;
|
|
10
|
+
const b4a_1 = __importDefault(require("b4a"));
|
|
11
|
+
const errors_js_1 = require("./errors.js");
|
|
12
|
+
const files_js_1 = require("./files.js");
|
|
13
|
+
const commit_journal_js_1 = require("./storage/commit-journal.js");
|
|
14
|
+
exports.MAX_SYMLINK_SELECTOR_BYTES = 200;
|
|
15
|
+
function invalid(message, cause = null) {
|
|
16
|
+
return new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.PROTOCOL_INVALID, message, cause);
|
|
17
|
+
}
|
|
18
|
+
function assertLinkName(value) {
|
|
19
|
+
if (typeof value !== 'string') {
|
|
20
|
+
throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.INVALID_FILENAME, 'Invalid symlink name');
|
|
21
|
+
}
|
|
22
|
+
const name = (0, files_js_1.validateBasename)(value);
|
|
23
|
+
if ((0, files_js_1.isReservedHistoryName)(name)) {
|
|
24
|
+
throw new errors_js_1.SwarmDeployError(errors_js_1.ERRORS.INVALID_FILENAME, 'Reserved symlink name');
|
|
25
|
+
}
|
|
26
|
+
return name;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Validates and snapshots operator symlink rules. The regex source is bounded,
|
|
30
|
+
* has no flags, and runs only against validated 100-byte artifact basenames; it
|
|
31
|
+
* is trusted operator configuration, and an invalid one is a startup error.
|
|
32
|
+
*/
|
|
33
|
+
function compileSymlinkRules(rules) {
|
|
34
|
+
if (rules === null || rules === undefined)
|
|
35
|
+
return Object.freeze([]);
|
|
36
|
+
if (typeof rules === 'string' ||
|
|
37
|
+
typeof rules !== 'object' ||
|
|
38
|
+
typeof rules[Symbol.iterator] !== 'function') {
|
|
39
|
+
throw invalid('Invalid symlink rules');
|
|
40
|
+
}
|
|
41
|
+
const compiled = [];
|
|
42
|
+
const names = new Set();
|
|
43
|
+
for (const rule of rules) {
|
|
44
|
+
if (!rule || typeof rule !== 'object' || Array.isArray(rule)) {
|
|
45
|
+
throw invalid('Invalid symlink rule');
|
|
46
|
+
}
|
|
47
|
+
const keys = Object.keys(rule).sort();
|
|
48
|
+
if (keys.length !== 2 || keys[0] !== 'name' || keys[1] !== 'selector') {
|
|
49
|
+
throw invalid('Invalid symlink rule');
|
|
50
|
+
}
|
|
51
|
+
const { selector, name: rawName } = rule;
|
|
52
|
+
const name = assertLinkName(rawName);
|
|
53
|
+
if (names.has(name))
|
|
54
|
+
throw invalid('Duplicate symlink name');
|
|
55
|
+
names.add(name);
|
|
56
|
+
if (typeof selector !== 'string' || selector.length === 0) {
|
|
57
|
+
throw invalid('Invalid symlink selector');
|
|
58
|
+
}
|
|
59
|
+
if (b4a_1.default.from(selector).byteLength > exports.MAX_SYMLINK_SELECTOR_BYTES) {
|
|
60
|
+
throw invalid('Invalid symlink selector');
|
|
61
|
+
}
|
|
62
|
+
if (selector.length >= 2 && selector.startsWith('/') && selector.endsWith('/')) {
|
|
63
|
+
const source = selector.slice(1, -1);
|
|
64
|
+
if (source.length === 0)
|
|
65
|
+
throw invalid('Invalid symlink selector');
|
|
66
|
+
let pattern;
|
|
67
|
+
try {
|
|
68
|
+
pattern = new RegExp(source);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
throw invalid('Invalid symlink selector', error);
|
|
72
|
+
}
|
|
73
|
+
compiled.push(Object.freeze({
|
|
74
|
+
selector,
|
|
75
|
+
name,
|
|
76
|
+
exact: null,
|
|
77
|
+
matches: (candidate) => pattern.test(candidate)
|
|
78
|
+
}));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const exact = (0, files_js_1.validateBasename)(selector);
|
|
82
|
+
if ((0, files_js_1.isReservedHistoryName)(exact))
|
|
83
|
+
throw invalid('Reserved symlink selector');
|
|
84
|
+
if (exact === name)
|
|
85
|
+
throw invalid('Symlink selector equals its own name');
|
|
86
|
+
compiled.push(Object.freeze({
|
|
87
|
+
selector,
|
|
88
|
+
name,
|
|
89
|
+
exact,
|
|
90
|
+
matches: (candidate) => candidate === exact
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
return Object.freeze(compiled);
|
|
94
|
+
}
|
|
95
|
+
function symlinkRuleNames(rules) {
|
|
96
|
+
return new Set(rules.map((rule) => rule.name));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Computes the desired links from validated commit records only. A rule with no
|
|
100
|
+
* match is dormant. Replacement history names never match, and a candidate
|
|
101
|
+
* whose name equals its own link name is skipped.
|
|
102
|
+
*/
|
|
103
|
+
function selectDesiredLinks(rules, records) {
|
|
104
|
+
const desired = [];
|
|
105
|
+
for (const rule of rules) {
|
|
106
|
+
let selected = null;
|
|
107
|
+
for (const record of records) {
|
|
108
|
+
if ((0, files_js_1.isReservedHistoryName)(record.name))
|
|
109
|
+
continue;
|
|
110
|
+
if (record.name === rule.name)
|
|
111
|
+
continue;
|
|
112
|
+
if (!rule.matches(record.name))
|
|
113
|
+
continue;
|
|
114
|
+
if (selected === null || (0, commit_journal_js_1.compareCommitOrder)(record, selected) < 0)
|
|
115
|
+
selected = record;
|
|
116
|
+
}
|
|
117
|
+
if (selected === null)
|
|
118
|
+
continue;
|
|
119
|
+
desired.push({
|
|
120
|
+
name: rule.name,
|
|
121
|
+
target: selected.name,
|
|
122
|
+
transferId: selected.transferId,
|
|
123
|
+
targetKind: (0, commit_journal_js_1.commitRecordKind)(selected)
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return desired;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=symlinks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symlinks.js","sourceRoot":"","sources":["../src/symlinks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type ErrorCode } from '../errors.js';
|
|
2
|
+
export declare const CONTROL_VERSION = 1;
|
|
3
|
+
export declare const MAX_CONTROL_RECORD_BYTES: number;
|
|
4
|
+
export interface MetadataRecord {
|
|
5
|
+
v: typeof CONTROL_VERSION;
|
|
6
|
+
name: string;
|
|
7
|
+
sourceParent?: string;
|
|
8
|
+
fileSize: number;
|
|
9
|
+
fileSha256: string;
|
|
10
|
+
tarSize: number;
|
|
11
|
+
tarSha256: string;
|
|
12
|
+
transferId: string;
|
|
13
|
+
reset: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A recursive directory offer. The key set is disjoint from the file record's,
|
|
17
|
+
* so an older server's `exactKeys()` check rejects it before touching storage.
|
|
18
|
+
*/
|
|
19
|
+
export interface TreeMetadataRecord {
|
|
20
|
+
v: typeof CONTROL_VERSION;
|
|
21
|
+
kind: 'directory';
|
|
22
|
+
name: string;
|
|
23
|
+
sourceParent?: string;
|
|
24
|
+
entryCount: number;
|
|
25
|
+
payloadBytes: number;
|
|
26
|
+
treeSha256: string;
|
|
27
|
+
tarSize: number;
|
|
28
|
+
tarSha256: string;
|
|
29
|
+
transferId: string;
|
|
30
|
+
reset: boolean;
|
|
31
|
+
}
|
|
32
|
+
export type AnyMetadataRecord = MetadataRecord | TreeMetadataRecord;
|
|
33
|
+
export declare function isTreeMetadata(value: AnyMetadataRecord): value is TreeMetadataRecord;
|
|
34
|
+
export type AdmissionRecord = {
|
|
35
|
+
v: typeof CONTROL_VERSION;
|
|
36
|
+
status: 'ACCEPT';
|
|
37
|
+
offset: 0;
|
|
38
|
+
} | {
|
|
39
|
+
v: typeof CONTROL_VERSION;
|
|
40
|
+
status: 'RESUME';
|
|
41
|
+
offset: number;
|
|
42
|
+
prefixSha256: string;
|
|
43
|
+
} | {
|
|
44
|
+
v: typeof CONTROL_VERSION;
|
|
45
|
+
status: 'VERIFIED';
|
|
46
|
+
} | {
|
|
47
|
+
v: typeof CONTROL_VERSION;
|
|
48
|
+
status: 'ALREADY_COMMITTED';
|
|
49
|
+
} | {
|
|
50
|
+
v: typeof CONTROL_VERSION;
|
|
51
|
+
status: 'REJECTED';
|
|
52
|
+
code: ErrorCode;
|
|
53
|
+
};
|
|
54
|
+
export type FinalRecord = {
|
|
55
|
+
v: typeof CONTROL_VERSION;
|
|
56
|
+
status: 'COMMITTED';
|
|
57
|
+
} | {
|
|
58
|
+
v: typeof CONTROL_VERSION;
|
|
59
|
+
status: 'FAILED';
|
|
60
|
+
code: ErrorCode;
|
|
61
|
+
};
|
|
62
|
+
export declare function assertSourceParent(value: unknown): asserts value is string;
|
|
63
|
+
export declare function encodeMetadataRecord(value: MetadataRecord): Buffer;
|
|
64
|
+
export declare function decodeMetadataRecord(bytes: Uint8Array): MetadataRecord;
|
|
65
|
+
export declare function encodeTreeMetadataRecord(value: TreeMetadataRecord): Buffer;
|
|
66
|
+
export declare function decodeTreeMetadataRecord(bytes: Uint8Array): TreeMetadataRecord;
|
|
67
|
+
export declare function encodeAnyMetadataRecord(value: AnyMetadataRecord): Buffer;
|
|
68
|
+
export declare function decodeAnyMetadataRecord(bytes: Uint8Array): AnyMetadataRecord;
|
|
69
|
+
export declare function encodeAdmissionRecord(value: AdmissionRecord): Buffer;
|
|
70
|
+
export declare function decodeAdmissionRecord(bytes: Uint8Array): AdmissionRecord;
|
|
71
|
+
export declare function encodeFinalRecord(value: FinalRecord): Buffer;
|
|
72
|
+
export declare function decodeFinalRecord(bytes: Uint8Array): FinalRecord;
|
|
73
|
+
export declare function encodeControlFrame(record: Uint8Array): Buffer;
|