@smthrs/artifacts 0.0.0-stage → 1.0.0-rc.4
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 +77 -0
- package/LICENSE +21 -0
- package/README.md +173 -2
- package/dist/cjs/ArtifactBackupLease.d.ts +34 -0
- package/dist/cjs/ArtifactBackupLease.d.ts.map +1 -0
- package/dist/cjs/ArtifactBackupLease.js +175 -0
- package/dist/cjs/ArtifactBackupLease.js.map +7 -0
- package/dist/cjs/ArtifactStore.d.ts +129 -0
- package/dist/cjs/ArtifactStore.d.ts.map +1 -0
- package/dist/cjs/ArtifactStore.js +73 -0
- package/dist/cjs/ArtifactStore.js.map +7 -0
- package/dist/cjs/ArtifactStoreError.d.ts +82 -0
- package/dist/cjs/ArtifactStoreError.d.ts.map +1 -0
- package/dist/cjs/ArtifactStoreError.js +72 -0
- package/dist/cjs/ArtifactStoreError.js.map +7 -0
- package/dist/cjs/ArtifactStoreMetrics.d.ts +70 -0
- package/dist/cjs/ArtifactStoreMetrics.d.ts.map +1 -0
- package/dist/cjs/ArtifactStoreMetrics.js +51 -0
- package/dist/cjs/ArtifactStoreMetrics.js.map +7 -0
- package/dist/cjs/ArtifactSweep.d.ts +185 -0
- package/dist/cjs/ArtifactSweep.d.ts.map +1 -0
- package/dist/cjs/ArtifactSweep.js +163 -0
- package/dist/cjs/ArtifactSweep.js.map +7 -0
- package/dist/cjs/CombinedArtifacts.d.ts +103 -0
- package/dist/cjs/CombinedArtifacts.d.ts.map +1 -0
- package/dist/cjs/CombinedArtifacts.js +189 -0
- package/dist/cjs/CombinedArtifacts.js.map +7 -0
- package/dist/cjs/FileLease.d.ts +113 -0
- package/dist/cjs/FileLease.d.ts.map +1 -0
- package/dist/cjs/FileLease.js +201 -0
- package/dist/cjs/FileLease.js.map +7 -0
- package/dist/cjs/FileSystemArtifactStore.d.ts +93 -0
- package/dist/cjs/FileSystemArtifactStore.d.ts.map +1 -0
- package/dist/cjs/FileSystemArtifactStore.js +276 -0
- package/dist/cjs/FileSystemArtifactStore.js.map +7 -0
- package/dist/cjs/MemoryArtifactStore.d.ts +25 -0
- package/dist/cjs/MemoryArtifactStore.d.ts.map +1 -0
- package/dist/cjs/MemoryArtifactStore.js +84 -0
- package/dist/cjs/MemoryArtifactStore.js.map +7 -0
- package/dist/cjs/NoopArtifactStore.d.ts +17 -0
- package/dist/cjs/NoopArtifactStore.d.ts.map +1 -0
- package/dist/cjs/NoopArtifactStore.js +46 -0
- package/dist/cjs/NoopArtifactStore.js.map +7 -0
- package/dist/cjs/RemoteArtifacts.d.ts +237 -0
- package/dist/cjs/RemoteArtifacts.d.ts.map +1 -0
- package/dist/cjs/RemoteArtifacts.js +405 -0
- package/dist/cjs/RemoteArtifacts.js.map +7 -0
- package/dist/cjs/index.d.ts +65 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +47 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/ArtifactLocks.d.ts +30 -0
- package/dist/cjs/internal/ArtifactLocks.d.ts.map +1 -0
- package/dist/cjs/internal/ArtifactLocks.js +93 -0
- package/dist/cjs/internal/ArtifactLocks.js.map +7 -0
- package/dist/cjs/internal/ArtifactPath.d.ts +19 -0
- package/dist/cjs/internal/ArtifactPath.d.ts.map +1 -0
- package/dist/cjs/internal/ArtifactPath.js +74 -0
- package/dist/cjs/internal/ArtifactPath.js.map +7 -0
- package/dist/cjs/measureBytes.d.ts +18 -0
- package/dist/cjs/measureBytes.d.ts.map +1 -0
- package/dist/cjs/measureBytes.js +43 -0
- package/dist/cjs/measureBytes.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/snapshotBytes.d.ts +19 -0
- package/dist/cjs/snapshotBytes.d.ts.map +1 -0
- package/dist/cjs/snapshotBytes.js +41 -0
- package/dist/cjs/snapshotBytes.js.map +7 -0
- package/dist/cjs/validateDigest.d.ts +22 -0
- package/dist/cjs/validateDigest.d.ts.map +1 -0
- package/dist/cjs/validateDigest.js +42 -0
- package/dist/cjs/validateDigest.js.map +7 -0
- package/dist/esm/ArtifactBackupLease.d.ts +34 -0
- package/dist/esm/ArtifactBackupLease.d.ts.map +1 -0
- package/dist/esm/ArtifactBackupLease.js +117 -0
- package/dist/esm/ArtifactBackupLease.js.map +1 -0
- package/dist/esm/ArtifactStore.d.ts +129 -0
- package/dist/esm/ArtifactStore.d.ts.map +1 -0
- package/dist/esm/ArtifactStore.js +83 -0
- package/dist/esm/ArtifactStore.js.map +1 -0
- package/dist/esm/ArtifactStoreError.d.ts +82 -0
- package/dist/esm/ArtifactStoreError.d.ts.map +1 -0
- package/dist/esm/ArtifactStoreError.js +77 -0
- package/dist/esm/ArtifactStoreError.js.map +1 -0
- package/dist/esm/ArtifactStoreMetrics.d.ts +70 -0
- package/dist/esm/ArtifactStoreMetrics.d.ts.map +1 -0
- package/dist/esm/ArtifactStoreMetrics.js +77 -0
- package/dist/esm/ArtifactStoreMetrics.js.map +1 -0
- package/dist/esm/ArtifactSweep.d.ts +185 -0
- package/dist/esm/ArtifactSweep.d.ts.map +1 -0
- package/dist/esm/ArtifactSweep.js +202 -0
- package/dist/esm/ArtifactSweep.js.map +1 -0
- package/dist/esm/CombinedArtifacts.d.ts +103 -0
- package/dist/esm/CombinedArtifacts.d.ts.map +1 -0
- package/dist/esm/CombinedArtifacts.js +214 -0
- package/dist/esm/CombinedArtifacts.js.map +1 -0
- package/dist/esm/FileLease.d.ts +113 -0
- package/dist/esm/FileLease.d.ts.map +1 -0
- package/dist/esm/FileLease.js +239 -0
- package/dist/esm/FileLease.js.map +1 -0
- package/dist/esm/FileSystemArtifactStore.d.ts +93 -0
- package/dist/esm/FileSystemArtifactStore.d.ts.map +1 -0
- package/dist/esm/FileSystemArtifactStore.js +336 -0
- package/dist/esm/FileSystemArtifactStore.js.map +1 -0
- package/dist/esm/MemoryArtifactStore.d.ts +25 -0
- package/dist/esm/MemoryArtifactStore.d.ts.map +1 -0
- package/dist/esm/MemoryArtifactStore.js +67 -0
- package/dist/esm/MemoryArtifactStore.js.map +1 -0
- package/dist/esm/NoopArtifactStore.d.ts +17 -0
- package/dist/esm/NoopArtifactStore.d.ts.map +1 -0
- package/dist/esm/NoopArtifactStore.js +27 -0
- package/dist/esm/NoopArtifactStore.js.map +1 -0
- package/dist/esm/RemoteArtifacts.d.ts +237 -0
- package/dist/esm/RemoteArtifacts.d.ts.map +1 -0
- package/dist/esm/RemoteArtifacts.js +554 -0
- package/dist/esm/RemoteArtifacts.js.map +1 -0
- package/dist/esm/index.d.ts +65 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +65 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/ArtifactLocks.d.ts +30 -0
- package/dist/esm/internal/ArtifactLocks.d.ts.map +1 -0
- package/dist/esm/internal/ArtifactLocks.js +86 -0
- package/dist/esm/internal/ArtifactLocks.js.map +1 -0
- package/dist/esm/internal/ArtifactPath.d.ts +19 -0
- package/dist/esm/internal/ArtifactPath.d.ts.map +1 -0
- package/dist/esm/internal/ArtifactPath.js +59 -0
- package/dist/esm/internal/ArtifactPath.js.map +1 -0
- package/dist/esm/measureBytes.d.ts +18 -0
- package/dist/esm/measureBytes.d.ts.map +1 -0
- package/dist/esm/measureBytes.js +22 -0
- package/dist/esm/measureBytes.js.map +1 -0
- package/dist/esm/snapshotBytes.d.ts +19 -0
- package/dist/esm/snapshotBytes.d.ts.map +1 -0
- package/dist/esm/snapshotBytes.js +30 -0
- package/dist/esm/snapshotBytes.js.map +1 -0
- package/dist/esm/validateDigest.d.ts +22 -0
- package/dist/esm/validateDigest.d.ts.map +1 -0
- package/dist/esm/validateDigest.js +26 -0
- package/dist/esm/validateDigest.js.map +1 -0
- package/docs/README.md +200 -0
- package/docs/api.md +474 -0
- package/docs/concepts/content-addressing.md +174 -0
- package/docs/concepts/coordination.md +141 -0
- package/docs/concepts/tiers.md +147 -0
- package/docs/guides/fence-a-backup.md +123 -0
- package/docs/guides/reclaim-disk-space.md +143 -0
- package/docs/guides/serve-the-artifact-protocol.md +140 -0
- package/docs/guides/share-artifacts-across-machines.md +180 -0
- package/docs/guides/test-against-an-artifact-store.md +158 -0
- package/docs/installation.md +98 -0
- package/docs/quickstart.md +182 -0
- package/docs/troubleshooting.md +232 -0
- package/package.json +179 -3
- package/src/ArtifactBackupLease.ts +213 -0
- package/src/ArtifactStore.ts +152 -0
- package/src/ArtifactStoreError.ts +96 -0
- package/src/ArtifactStoreMetrics.ts +83 -0
- package/src/ArtifactSweep.ts +336 -0
- package/src/CombinedArtifacts.ts +369 -0
- package/src/FileLease.ts +380 -0
- package/src/FileSystemArtifactStore.ts +459 -0
- package/src/MemoryArtifactStore.ts +83 -0
- package/src/NoopArtifactStore.ts +30 -0
- package/src/RemoteArtifacts.ts +809 -0
- package/src/index.ts +71 -0
- package/src/internal/ArtifactLocks.ts +110 -0
- package/src/internal/ArtifactPath.ts +72 -0
- package/src/measureBytes.ts +28 -0
- package/src/snapshotBytes.ts +34 -0
- package/src/validateDigest.ts +31 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var ArtifactStore_exports = {};
|
|
30
|
+
__export(ArtifactStore_exports, {
|
|
31
|
+
ArtifactCorruption: () => import_ArtifactStoreError.ArtifactCorruption,
|
|
32
|
+
ArtifactMissing: () => import_ArtifactStoreError.ArtifactMissing,
|
|
33
|
+
ArtifactStore: () => ArtifactStore,
|
|
34
|
+
ArtifactStoreError: () => import_ArtifactStoreError.ArtifactStoreError,
|
|
35
|
+
ArtifactStoreErrorCode: () => import_ArtifactStoreError.ArtifactStoreErrorCode,
|
|
36
|
+
Digest: () => Digest,
|
|
37
|
+
defaultDirectory: () => import_FileSystemArtifactStore2.defaultDirectory,
|
|
38
|
+
layerFileSystem: () => layerFileSystem,
|
|
39
|
+
layerMemory: () => layerMemory,
|
|
40
|
+
layerNoop: () => layerNoop,
|
|
41
|
+
makeFileSystem: () => import_FileSystemArtifactStore2.makeFileSystem,
|
|
42
|
+
makeMemory: () => import_MemoryArtifactStore2.makeMemory,
|
|
43
|
+
makeNoop: () => import_NoopArtifactStore2.makeNoop,
|
|
44
|
+
measureBytes: () => import_measureBytes.measureBytes,
|
|
45
|
+
snapshotBytes: () => import_snapshotBytes.snapshotBytes,
|
|
46
|
+
validateDigest: () => import_validateDigest.validateDigest
|
|
47
|
+
});
|
|
48
|
+
module.exports = __toCommonJS(ArtifactStore_exports);
|
|
49
|
+
var import_crypto = require("@smthrs/crypto");
|
|
50
|
+
var Context = __toESM(require("effect/Context"), 1);
|
|
51
|
+
var Effect = __toESM(require("effect/Effect"), 1);
|
|
52
|
+
var FileSystem = __toESM(require("effect/FileSystem"), 1);
|
|
53
|
+
var Layer = __toESM(require("effect/Layer"), 1);
|
|
54
|
+
var Path = __toESM(require("effect/Path"), 1);
|
|
55
|
+
var import_FileSystemArtifactStore = require("./FileSystemArtifactStore.js");
|
|
56
|
+
var import_MemoryArtifactStore = require("./MemoryArtifactStore.js");
|
|
57
|
+
var import_NoopArtifactStore = require("./NoopArtifactStore.js");
|
|
58
|
+
var import_ArtifactStoreError = require("./ArtifactStoreError.js");
|
|
59
|
+
var import_FileSystemArtifactStore2 = require("./FileSystemArtifactStore.js");
|
|
60
|
+
var import_measureBytes = require("./measureBytes.js");
|
|
61
|
+
var import_MemoryArtifactStore2 = require("./MemoryArtifactStore.js");
|
|
62
|
+
var import_NoopArtifactStore2 = require("./NoopArtifactStore.js");
|
|
63
|
+
var import_snapshotBytes = require("./snapshotBytes.js");
|
|
64
|
+
var import_validateDigest = require("./validateDigest.js");
|
|
65
|
+
const Digest = import_crypto.Sha256.Digest;
|
|
66
|
+
class ArtifactStore extends Context.Service()("@smthrs/artifacts/ArtifactStore") {
|
|
67
|
+
}
|
|
68
|
+
const layerFileSystem = (options = {}) => Layer.effect(ArtifactStore)(Effect.gen(function* () {
|
|
69
|
+
return (0, import_FileSystemArtifactStore.makeFileSystem)(yield* FileSystem.FileSystem, yield* Path.Path, options);
|
|
70
|
+
}));
|
|
71
|
+
const layerMemory = Layer.effect(ArtifactStore)(Effect.sync(import_MemoryArtifactStore.makeMemory));
|
|
72
|
+
const layerNoop = (overrides = {}) => Layer.succeed(ArtifactStore)((0, import_NoopArtifactStore.makeNoop)(overrides));
|
|
73
|
+
//# sourceMappingURL=ArtifactStore.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ArtifactStore.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The content-addressed artifact store: bytes addressed by their own SHA-256\n * digest.\n *\n * It is deliberately *not* the step cache: the step cache maps a step key to a\n * recorded result, while large result bytes live here under their digest. The\n * two tiers remain separate because artifacts must be published before a cache\n * record may reference them. See the package README and\n * {@link https://smithers.sh/docs/concepts/content-addressing | step-key documentation}.\n *\n * This module defines the contract: the address schema, the service interface\n * and tag, and one layer per local implementation. The errors, byte helpers,\n * and implementations are defined in the modules named for them and re-exported\n * here unchanged, so this subpath and the root `ArtifactStore` namespace keep\n * their whole surface.\n *\n * @since 1.0.0-rc.0\n */\n\nimport { Sha256 } from \"@smthrs/crypto\"\nimport * as Context from \"effect/Context\"\nimport type * as Crypto from \"effect/Crypto\"\nimport * as Effect from \"effect/Effect\"\nimport * as FileSystem from \"effect/FileSystem\"\nimport * as Layer from \"effect/Layer\"\nimport * as Path from \"effect/Path\"\nimport type { ArtifactCorruption, ArtifactMissing, ArtifactStoreError } from \"./ArtifactStoreError.ts\"\nimport { type FileSystemOptions, makeFileSystem } from \"./FileSystemArtifactStore.ts\"\nimport { makeMemory } from \"./MemoryArtifactStore.ts\"\nimport { makeNoop } from \"./NoopArtifactStore.ts\"\n\nexport {\n ArtifactCorruption,\n ArtifactMissing,\n ArtifactStoreError,\n ArtifactStoreErrorCode\n} from \"./ArtifactStoreError.ts\"\nexport { defaultDirectory, type FileSystemOptions, makeFileSystem } from \"./FileSystemArtifactStore.ts\"\nexport { measureBytes } from \"./measureBytes.ts\"\nexport { makeMemory } from \"./MemoryArtifactStore.ts\"\nexport { makeNoop } from \"./NoopArtifactStore.ts\"\nexport { snapshotBytes } from \"./snapshotBytes.ts\"\nexport { validateDigest } from \"./validateDigest.ts\"\n\n/**\n * Schema for a content address: exactly 64 lowercase hexadecimal SHA-256\n * characters, branded by `@smthrs/crypto`. Re-exported so a consumer never has\n * to reach past this package for the address type it stores under.\n *\n * @category schemas\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const Digest = Sha256.Digest\n\n/**\n * A content address produced by this store.\n *\n * Read operations accept a plain `string` rather than this brand on purpose: a\n * digest read back out of a durable row is untrusted input, so the store\n * validates it (see {@link ArtifactStoreError}'s `invalid_digest` code) instead\n * of asking every caller to re-brand a persisted column. `put` returns the\n * brand, because it measured the bytes itself.\n *\n * @category models\n * @since 1.0.0-rc.0\n * @slop\n */\nexport type Digest = typeof Sha256.Digest.Type\n\n/**\n * Content-addressed blob storage.\n *\n * The contract's ergonomics follow Effect's own `KeyValueStore`\n * (`effect/unstable/persistence/KeyValueStore`): a small set of total\n * operations over one address space, with a single typed error family, so a\n * memory, filesystem, or network implementation is the same shape.\n * `findMissing` is Bazel's `MissingDigestsFinder` \u2014 one batched round trip\n * whose result is guaranteed to be a subset of its input \u2014 because a\n * per-digest existence probe over a network tier is the wrong shape entirely.\n *\n * @category models\n * @since 1.0.0-rc.0\n * @slop\n */\nexport interface Service {\n /**\n * Stores `bytes` under their own SHA-256 digest and returns that address.\n * Storing the same bytes twice is idempotent.\n */\n readonly put: (bytes: Uint8Array) => Effect.Effect<Digest, ArtifactStoreError, Crypto.Crypto>\n /**\n * Reads the bytes stored at `digest`, verifying that they still hash to it.\n */\n readonly get: (\n digest: string\n ) => Effect.Effect<Uint8Array, ArtifactMissing | ArtifactCorruption | ArtifactStoreError, Crypto.Crypto>\n /** Whether this tier holds an artifact at `digest`. */\n readonly has: (digest: string) => Effect.Effect<boolean, ArtifactStoreError>\n /**\n * Which of `digests` this tier does not hold. The returned array is\n * guaranteed to be a subset of the input and free of duplicates.\n */\n readonly findMissing: (\n digests: Iterable<string>\n ) => Effect.Effect<Array<string>, ArtifactStoreError>\n}\n\n/**\n * Service tag for the content-addressed artifact store.\n *\n * The identity string equals this module's package path, per the house rule\n * that an identity is the defining module path.\n *\n * @category services\n * @since 1.0.0-rc.0\n * @slop\n */\nexport class ArtifactStore extends Context.Service<ArtifactStore, Service>()(\"@smthrs/artifacts/ArtifactStore\") {}\n\n/**\n * Provides the filesystem-backed artifact store.\n *\n * @category layers\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const layerFileSystem = (\n options: FileSystemOptions = {}\n): Layer.Layer<ArtifactStore, never, FileSystem.FileSystem | Path.Path> =>\n Layer.effect(ArtifactStore)(Effect.gen(function*() {\n return makeFileSystem(yield* FileSystem.FileSystem, yield* Path.Path, options)\n }))\n\n/**\n * Provides an in-memory artifact store.\n *\n * @category layers\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const layerMemory: Layer.Layer<ArtifactStore> = Layer.effect(ArtifactStore)(Effect.sync(makeMemory))\n\n/**\n * Provides a no-op artifact store.\n *\n * @category layers\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const layerNoop = (overrides: Partial<Service> = {}): Layer.Layer<ArtifactStore> =>\n Layer.succeed(ArtifactStore)(makeNoop(overrides))\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBA,oBAAuB;AACvB,cAAyB;AAEzB,aAAwB;AACxB,iBAA4B;AAC5B,YAAuB;AACvB,WAAsB;AAEtB,qCAAuD;AACvD,iCAA2B;AAC3B,+BAAyB;AAEzB,gCAKO;AACP,IAAAA,kCAAyE;AACzE,0BAA6B;AAC7B,IAAAC,8BAA2B;AAC3B,IAAAC,4BAAyB;AACzB,2BAA8B;AAC9B,4BAA+B;AAWxB,MAAM,SAAS,qBAAO;AAiEtB,MAAM,sBAAsB,QAAQ,QAAgC,EAAE,iCAAiC,EAAE;AAAC;AAS1G,MAAM,kBAAkB,CAC7B,UAA6B,CAAC,MAE9B,MAAM,OAAO,aAAa,EAAE,OAAO,IAAI,aAAY;AACjD,aAAO,+CAAe,OAAO,WAAW,YAAY,OAAO,KAAK,MAAM,OAAO;AAC/E,CAAC,CAAC;AASG,MAAM,cAA0C,MAAM,OAAO,aAAa,EAAE,OAAO,KAAK,qCAAU,CAAC;AASnG,MAAM,YAAY,CAAC,YAA8B,CAAC,MACvD,MAAM,QAAQ,aAAa,MAAE,mCAAS,SAAS,CAAC;",
|
|
6
|
+
"names": ["import_FileSystemArtifactStore", "import_MemoryArtifactStore", "import_NoopArtifactStore"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The artifact store's typed failures: a refusal of the store itself, the
|
|
3
|
+
* typed miss, and a digest mismatch, with the stable codes a refusal carries.
|
|
4
|
+
*
|
|
5
|
+
* @since 1.0.0-rc.0
|
|
6
|
+
*/
|
|
7
|
+
import * as Schema from "effect/Schema";
|
|
8
|
+
/**
|
|
9
|
+
* Stable error codes returned by artifact store operations.
|
|
10
|
+
*
|
|
11
|
+
* @category models
|
|
12
|
+
* @since 1.0.0-rc.0
|
|
13
|
+
* @slop
|
|
14
|
+
*/
|
|
15
|
+
export declare const ArtifactStoreErrorCode: Schema.Literals<readonly ["digest_failed", "invalid_configuration", "invalid_digest", "unavailable", "transport_failed"]>;
|
|
16
|
+
/**
|
|
17
|
+
* Stable error codes returned by artifact store operations.
|
|
18
|
+
*
|
|
19
|
+
* @category models
|
|
20
|
+
* @since 1.0.0-rc.0
|
|
21
|
+
* @slop
|
|
22
|
+
*/
|
|
23
|
+
export type ArtifactStoreErrorCode = typeof ArtifactStoreErrorCode.Type;
|
|
24
|
+
declare const ArtifactStoreError_base: Schema.Class<ArtifactStoreError, Schema.TaggedStruct<"@smthrs/artifacts/ArtifactStoreError", {
|
|
25
|
+
readonly code: Schema.Literals<readonly ["digest_failed", "invalid_configuration", "invalid_digest", "unavailable", "transport_failed"]>;
|
|
26
|
+
readonly message: Schema.String;
|
|
27
|
+
readonly cause: Schema.optional<Schema.Unknown>;
|
|
28
|
+
}>, import("effect/Cause").YieldableError>;
|
|
29
|
+
/**
|
|
30
|
+
* A typed failure of the store itself: the host or crypto provider refused an
|
|
31
|
+
* operation, the remote tier refused a request, or the caller supplied invalid
|
|
32
|
+
* configuration or an invalid content address.
|
|
33
|
+
*
|
|
34
|
+
* Distinct from {@link ArtifactMissing} and {@link ArtifactCorruption} on
|
|
35
|
+
* purpose. A miss is an ordinary, expected outcome that a second tier may
|
|
36
|
+
* still satisfy; corruption is an integrity violation of the store's strongest
|
|
37
|
+
* invariant. `invalid_configuration` and `invalid_digest` are permanent;
|
|
38
|
+
* retryability of host, crypto, and transport failures depends on the cause.
|
|
39
|
+
*
|
|
40
|
+
* @category errors
|
|
41
|
+
* @since 1.0.0-rc.0
|
|
42
|
+
* @slop
|
|
43
|
+
*/
|
|
44
|
+
export declare class ArtifactStoreError extends ArtifactStoreError_base {
|
|
45
|
+
}
|
|
46
|
+
declare const ArtifactMissing_base: Schema.Class<ArtifactMissing, Schema.TaggedStruct<"@smthrs/artifacts/ArtifactMissing", {
|
|
47
|
+
readonly code: Schema.Literal<"artifact_missing">;
|
|
48
|
+
readonly digest: Schema.brand<Schema.String, "@smthrs/crypto/Sha256/Digest">;
|
|
49
|
+
}>, import("effect/Cause").YieldableError>;
|
|
50
|
+
/**
|
|
51
|
+
* The typed miss: this tier holds no bytes at the requested address.
|
|
52
|
+
*
|
|
53
|
+
* A miss is not a failure of the store — it is the answer a read-through
|
|
54
|
+
* composition is built to act on, so it is a distinct tag rather than an
|
|
55
|
+
* `unavailable` code that a caller would have to string-match.
|
|
56
|
+
*
|
|
57
|
+
* @category errors
|
|
58
|
+
* @since 1.0.0-rc.0
|
|
59
|
+
* @slop
|
|
60
|
+
*/
|
|
61
|
+
export declare class ArtifactMissing extends ArtifactMissing_base {
|
|
62
|
+
}
|
|
63
|
+
declare const ArtifactCorruption_base: Schema.Class<ArtifactCorruption, Schema.TaggedStruct<"@smthrs/artifacts/ArtifactCorruption", {
|
|
64
|
+
readonly code: Schema.Literal<"artifact_corruption">;
|
|
65
|
+
readonly recordedDigest: Schema.brand<Schema.String, "@smthrs/crypto/Sha256/Digest">;
|
|
66
|
+
readonly measuredDigest: Schema.brand<Schema.String, "@smthrs/crypto/Sha256/Digest">;
|
|
67
|
+
}>, import("effect/Cause").YieldableError>;
|
|
68
|
+
/**
|
|
69
|
+
* Bytes stored at a content address no longer hash to it.
|
|
70
|
+
*
|
|
71
|
+
* Every read is digest-verified, so a truncated blob left by a crashing writer
|
|
72
|
+
* or by disk corruption is refused rather than handed back as if it were the
|
|
73
|
+
* recorded artifact.
|
|
74
|
+
*
|
|
75
|
+
* @category errors
|
|
76
|
+
* @since 1.0.0-rc.0
|
|
77
|
+
* @slop
|
|
78
|
+
*/
|
|
79
|
+
export declare class ArtifactCorruption extends ArtifactCorruption_base {
|
|
80
|
+
}
|
|
81
|
+
export {};
|
|
82
|
+
//# sourceMappingURL=ArtifactStoreError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ArtifactStoreError.d.ts","sourceRoot":"","sources":["../../src/ArtifactStoreError.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,2HAMjC,CAAA;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,sBAAsB,GAAG,OAAO,sBAAsB,CAAC,IAAI,CAAA;;;;;;AAEvE;;;;;;;;;;;;;;GAcG;AACH,qBAAa,kBAAmB,SAAQ,uBAOvC;CAAG;;;;;AAEJ;;;;;;;;;;GAUG;AACH,qBAAa,eAAgB,SAAQ,oBAMpC;CAAG;;;;;;AAEJ;;;;;;;;;;GAUG;AACH,qBAAa,kBAAmB,SAAQ,uBAOvC;CAAG"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var ArtifactStoreError_exports = {};
|
|
30
|
+
__export(ArtifactStoreError_exports, {
|
|
31
|
+
ArtifactCorruption: () => ArtifactCorruption,
|
|
32
|
+
ArtifactMissing: () => ArtifactMissing,
|
|
33
|
+
ArtifactStoreError: () => ArtifactStoreError,
|
|
34
|
+
ArtifactStoreErrorCode: () => ArtifactStoreErrorCode
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(ArtifactStoreError_exports);
|
|
37
|
+
var import_crypto = require("@smthrs/crypto");
|
|
38
|
+
var Schema = __toESM(require("effect/Schema"), 1);
|
|
39
|
+
const ArtifactStoreErrorCode = Schema.Literals([
|
|
40
|
+
"digest_failed",
|
|
41
|
+
"invalid_configuration",
|
|
42
|
+
"invalid_digest",
|
|
43
|
+
"unavailable",
|
|
44
|
+
"transport_failed"
|
|
45
|
+
]);
|
|
46
|
+
class ArtifactStoreError extends Schema.TaggedError()(
|
|
47
|
+
"@smthrs/artifacts/ArtifactStoreError",
|
|
48
|
+
{
|
|
49
|
+
code: ArtifactStoreErrorCode,
|
|
50
|
+
message: Schema.String,
|
|
51
|
+
cause: Schema.optional(Schema.Unknown)
|
|
52
|
+
}
|
|
53
|
+
) {
|
|
54
|
+
}
|
|
55
|
+
class ArtifactMissing extends Schema.TaggedError()(
|
|
56
|
+
"@smthrs/artifacts/ArtifactMissing",
|
|
57
|
+
{
|
|
58
|
+
code: Schema.Literal("artifact_missing"),
|
|
59
|
+
digest: import_crypto.Sha256.Digest
|
|
60
|
+
}
|
|
61
|
+
) {
|
|
62
|
+
}
|
|
63
|
+
class ArtifactCorruption extends Schema.TaggedError()(
|
|
64
|
+
"@smthrs/artifacts/ArtifactCorruption",
|
|
65
|
+
{
|
|
66
|
+
code: Schema.Literal("artifact_corruption"),
|
|
67
|
+
recordedDigest: import_crypto.Sha256.Digest,
|
|
68
|
+
measuredDigest: import_crypto.Sha256.Digest
|
|
69
|
+
}
|
|
70
|
+
) {
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=ArtifactStoreError.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ArtifactStoreError.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The artifact store's typed failures: a refusal of the store itself, the\n * typed miss, and a digest mismatch, with the stable codes a refusal carries.\n *\n * @since 1.0.0-rc.0\n */\n\nimport { Sha256 } from \"@smthrs/crypto\"\nimport * as Schema from \"effect/Schema\"\n\n/**\n * Stable error codes returned by artifact store operations.\n *\n * @category models\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const ArtifactStoreErrorCode = Schema.Literals([\n \"digest_failed\",\n \"invalid_configuration\",\n \"invalid_digest\",\n \"unavailable\",\n \"transport_failed\"\n])\n\n/**\n * Stable error codes returned by artifact store operations.\n *\n * @category models\n * @since 1.0.0-rc.0\n * @slop\n */\nexport type ArtifactStoreErrorCode = typeof ArtifactStoreErrorCode.Type\n\n/**\n * A typed failure of the store itself: the host or crypto provider refused an\n * operation, the remote tier refused a request, or the caller supplied invalid\n * configuration or an invalid content address.\n *\n * Distinct from {@link ArtifactMissing} and {@link ArtifactCorruption} on\n * purpose. A miss is an ordinary, expected outcome that a second tier may\n * still satisfy; corruption is an integrity violation of the store's strongest\n * invariant. `invalid_configuration` and `invalid_digest` are permanent;\n * retryability of host, crypto, and transport failures depends on the cause.\n *\n * @category errors\n * @since 1.0.0-rc.0\n * @slop\n */\nexport class ArtifactStoreError extends Schema.TaggedError<ArtifactStoreError>()(\n \"@smthrs/artifacts/ArtifactStoreError\",\n {\n code: ArtifactStoreErrorCode,\n message: Schema.String,\n cause: Schema.optional(Schema.Unknown)\n }\n) {}\n\n/**\n * The typed miss: this tier holds no bytes at the requested address.\n *\n * A miss is not a failure of the store \u2014 it is the answer a read-through\n * composition is built to act on, so it is a distinct tag rather than an\n * `unavailable` code that a caller would have to string-match.\n *\n * @category errors\n * @since 1.0.0-rc.0\n * @slop\n */\nexport class ArtifactMissing extends Schema.TaggedError<ArtifactMissing>()(\n \"@smthrs/artifacts/ArtifactMissing\",\n {\n code: Schema.Literal(\"artifact_missing\"),\n digest: Sha256.Digest\n }\n) {}\n\n/**\n * Bytes stored at a content address no longer hash to it.\n *\n * Every read is digest-verified, so a truncated blob left by a crashing writer\n * or by disk corruption is refused rather than handed back as if it were the\n * recorded artifact.\n *\n * @category errors\n * @since 1.0.0-rc.0\n * @slop\n */\nexport class ArtifactCorruption extends Schema.TaggedError<ArtifactCorruption>()(\n \"@smthrs/artifacts/ArtifactCorruption\",\n {\n code: Schema.Literal(\"artifact_corruption\"),\n recordedDigest: Sha256.Digest,\n measuredDigest: Sha256.Digest\n }\n) {}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,oBAAuB;AACvB,aAAwB;AASjB,MAAM,yBAAyB,OAAO,SAAS;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AA0BM,MAAM,2BAA2B,OAAO,YAAgC;AAAA,EAC7E;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS,OAAO;AAAA,IAChB,OAAO,OAAO,SAAS,OAAO,OAAO;AAAA,EACvC;AACF,EAAE;AAAC;AAaI,MAAM,wBAAwB,OAAO,YAA6B;AAAA,EACvE;AAAA,EACA;AAAA,IACE,MAAM,OAAO,QAAQ,kBAAkB;AAAA,IACvC,QAAQ,qBAAO;AAAA,EACjB;AACF,EAAE;AAAC;AAaI,MAAM,2BAA2B,OAAO,YAAgC;AAAA,EAC7E;AAAA,EACA;AAAA,IACE,MAAM,OAAO,QAAQ,qBAAqB;AAAA,IAC1C,gBAAgB,qBAAO;AAAA,IACvB,gBAAgB,qBAAO;AAAA,EACzB;AACF,EAAE;AAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard metric definitions for content-addressed artifact storage.
|
|
3
|
+
*
|
|
4
|
+
* This module only defines the metric handles, following the shape of Effect's
|
|
5
|
+
* `ClusterMetrics`. No exporter ships in this package; provide one — for
|
|
6
|
+
* example `@smthrs/observability` — and these counters appear in it.
|
|
7
|
+
*
|
|
8
|
+
* Only the local `ArtifactStore` implementations, filesystem and memory, update
|
|
9
|
+
* them. `RemoteArtifacts` is deliberately uninstrumented, and the counters carry
|
|
10
|
+
* no tier attribute, so read them as *local artifact store traffic* rather than
|
|
11
|
+
* as artifact operations:
|
|
12
|
+
*
|
|
13
|
+
* - a `CombinedArtifacts` read the local tier serves counts one get;
|
|
14
|
+
* - a read the shared tier serves counts NO get, because no local store
|
|
15
|
+
* answered it;
|
|
16
|
+
* - the write-back that materializes such a read counts a put, indistinguishable
|
|
17
|
+
* from a producer publishing new bytes.
|
|
18
|
+
*
|
|
19
|
+
* Attributing operations per tier needs the tier in the metric, which would
|
|
20
|
+
* change the published counter shape; until then this is what the numbers mean.
|
|
21
|
+
*
|
|
22
|
+
* The one shared-tier signal is {@link remoteFailure}: a `CombinedArtifacts`
|
|
23
|
+
* upload or write-back that was refused, or abandoned at its deadline.
|
|
24
|
+
*
|
|
25
|
+
* @since 1.0.0-rc.0
|
|
26
|
+
*/
|
|
27
|
+
import * as Metric from "effect/Metric";
|
|
28
|
+
/**
|
|
29
|
+
* Counter over successful artifact puts. A put that deduplicated against an
|
|
30
|
+
* existing verified blob still counts: the caller stored bytes and received
|
|
31
|
+
* an address either way.
|
|
32
|
+
*
|
|
33
|
+
* @category metrics
|
|
34
|
+
* @since 1.0.0-rc.0
|
|
35
|
+
* @slop
|
|
36
|
+
*/
|
|
37
|
+
export declare const puts: Metric.Counter<number>;
|
|
38
|
+
/**
|
|
39
|
+
* Counter over successful artifact gets. Missing and corrupt reads fail with
|
|
40
|
+
* their typed errors and are deliberately not counted here; they are error
|
|
41
|
+
* evidence, not throughput.
|
|
42
|
+
*
|
|
43
|
+
* @category metrics
|
|
44
|
+
* @since 1.0.0-rc.0
|
|
45
|
+
* @slop
|
|
46
|
+
*/
|
|
47
|
+
export declare const gets: Metric.Counter<number>;
|
|
48
|
+
/**
|
|
49
|
+
* Counter over opportunistic `CombinedArtifacts` transfers that failed or hit
|
|
50
|
+
* their deadline and were dropped: `put`'s upload to the shared tier and
|
|
51
|
+
* `get`'s write-back into the local tier.
|
|
52
|
+
*
|
|
53
|
+
* Read the attributed views on {@link remoteFailure}: every update carries an
|
|
54
|
+
* `operation` attribute, so this bare handle aggregates nothing and always
|
|
55
|
+
* reads zero.
|
|
56
|
+
*
|
|
57
|
+
* @category metrics
|
|
58
|
+
* @since 1.0.0-rc.1
|
|
59
|
+
*/
|
|
60
|
+
export declare const remoteFailures: Metric.Counter<number>;
|
|
61
|
+
/**
|
|
62
|
+
* `remoteFailures` views keyed by the dropped transfer.
|
|
63
|
+
*
|
|
64
|
+
* @category metrics
|
|
65
|
+
* @since 1.0.0-rc.1
|
|
66
|
+
*/
|
|
67
|
+
export declare const remoteFailure: {
|
|
68
|
+
readonly [Operation in "put" | "write_back"]: Metric.Metric<number, Metric.CounterState<number>>;
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=ArtifactStoreMetrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ArtifactStoreMetrics.d.ts","sourceRoot":"","sources":["../../src/ArtifactStoreMetrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC;;;;;;;;GAQG;AACH,eAAO,MAAM,IAAI,wBAEf,CAAA;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,IAAI,wBAEf,CAAA;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,wBAEzB,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE;IAC1B,QAAQ,EAAE,SAAS,IAAI,KAAK,GAAG,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;CAIjG,CAAA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var ArtifactStoreMetrics_exports = {};
|
|
30
|
+
__export(ArtifactStoreMetrics_exports, {
|
|
31
|
+
gets: () => gets,
|
|
32
|
+
puts: () => puts,
|
|
33
|
+
remoteFailure: () => remoteFailure,
|
|
34
|
+
remoteFailures: () => remoteFailures
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(ArtifactStoreMetrics_exports);
|
|
37
|
+
var Metric = __toESM(require("effect/Metric"), 1);
|
|
38
|
+
const puts = Metric.counter("flows_artifact_puts", {
|
|
39
|
+
description: "Successful artifact puts, including deduplicated ones"
|
|
40
|
+
});
|
|
41
|
+
const gets = Metric.counter("flows_artifact_gets", {
|
|
42
|
+
description: "Successful artifact gets"
|
|
43
|
+
});
|
|
44
|
+
const remoteFailures = Metric.counter("flows_artifact_remote_failures", {
|
|
45
|
+
description: "Dropped combined-artifact transfers by operation"
|
|
46
|
+
});
|
|
47
|
+
const remoteFailure = {
|
|
48
|
+
put: Metric.withAttributes(remoteFailures, { operation: "put" }),
|
|
49
|
+
write_back: Metric.withAttributes(remoteFailures, { operation: "write_back" })
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=ArtifactStoreMetrics.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ArtifactStoreMetrics.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Standard metric definitions for content-addressed artifact storage.\n *\n * This module only defines the metric handles, following the shape of Effect's\n * `ClusterMetrics`. No exporter ships in this package; provide one \u2014 for\n * example `@smthrs/observability` \u2014 and these counters appear in it.\n *\n * Only the local `ArtifactStore` implementations, filesystem and memory, update\n * them. `RemoteArtifacts` is deliberately uninstrumented, and the counters carry\n * no tier attribute, so read them as *local artifact store traffic* rather than\n * as artifact operations:\n *\n * - a `CombinedArtifacts` read the local tier serves counts one get;\n * - a read the shared tier serves counts NO get, because no local store\n * answered it;\n * - the write-back that materializes such a read counts a put, indistinguishable\n * from a producer publishing new bytes.\n *\n * Attributing operations per tier needs the tier in the metric, which would\n * change the published counter shape; until then this is what the numbers mean.\n *\n * The one shared-tier signal is {@link remoteFailure}: a `CombinedArtifacts`\n * upload or write-back that was refused, or abandoned at its deadline.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as Metric from \"effect/Metric\"\n\n/**\n * Counter over successful artifact puts. A put that deduplicated against an\n * existing verified blob still counts: the caller stored bytes and received\n * an address either way.\n *\n * @category metrics\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const puts = Metric.counter(\"flows_artifact_puts\", {\n description: \"Successful artifact puts, including deduplicated ones\"\n})\n\n/**\n * Counter over successful artifact gets. Missing and corrupt reads fail with\n * their typed errors and are deliberately not counted here; they are error\n * evidence, not throughput.\n *\n * @category metrics\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const gets = Metric.counter(\"flows_artifact_gets\", {\n description: \"Successful artifact gets\"\n})\n\n/**\n * Counter over opportunistic `CombinedArtifacts` transfers that failed or hit\n * their deadline and were dropped: `put`'s upload to the shared tier and\n * `get`'s write-back into the local tier.\n *\n * Read the attributed views on {@link remoteFailure}: every update carries an\n * `operation` attribute, so this bare handle aggregates nothing and always\n * reads zero.\n *\n * @category metrics\n * @since 1.0.0-rc.1\n */\nexport const remoteFailures = Metric.counter(\"flows_artifact_remote_failures\", {\n description: \"Dropped combined-artifact transfers by operation\"\n})\n\n/**\n * `remoteFailures` views keyed by the dropped transfer.\n *\n * @category metrics\n * @since 1.0.0-rc.1\n */\nexport const remoteFailure: {\n readonly [Operation in \"put\" | \"write_back\"]: Metric.Metric<number, Metric.CounterState<number>>\n} = {\n put: Metric.withAttributes(remoteFailures, { operation: \"put\" }),\n write_back: Metric.withAttributes(remoteFailures, { operation: \"write_back\" })\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BA,aAAwB;AAWjB,MAAM,OAAO,OAAO,QAAQ,uBAAuB;AAAA,EACxD,aAAa;AACf,CAAC;AAWM,MAAM,OAAO,OAAO,QAAQ,uBAAuB;AAAA,EACxD,aAAa;AACf,CAAC;AAcM,MAAM,iBAAiB,OAAO,QAAQ,kCAAkC;AAAA,EAC7E,aAAa;AACf,CAAC;AAQM,MAAM,gBAET;AAAA,EACF,KAAK,OAAO,eAAe,gBAAgB,EAAE,WAAW,MAAM,CAAC;AAAA,EAC/D,YAAY,OAAO,eAAe,gBAAgB,EAAE,WAAW,aAAa,CAAC;AAC/E;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sweep half of artifact garbage collection: enumerate this host's blobs
|
|
3
|
+
* and delete one, fenced by its age.
|
|
4
|
+
*
|
|
5
|
+
* This surface is deliberately NOT part of `ArtifactStore.Service`. The store
|
|
6
|
+
* contract is shared by memory, filesystem, and network tiers, and a remote
|
|
7
|
+
* tier can neither enumerate its address space nor accept a delete — Bazel
|
|
8
|
+
* draws the same line: its disk-cache collector
|
|
9
|
+
* (class `DiskCacheGarbageCollector` in
|
|
10
|
+
* `com.google.devtools.build.lib.remote.disk`) walks the local directory, while
|
|
11
|
+
* the remote tier owns its own retention. Only the
|
|
12
|
+
* host-local filesystem store implements this.
|
|
13
|
+
*
|
|
14
|
+
* The *policy* — which digests are live, how old a dead blob must be before it
|
|
15
|
+
* goes — belongs to the engine composition, which is the only place the
|
|
16
|
+
* durable roots (attempt rows, cache entries) are visible. This module ships
|
|
17
|
+
* mechanics alone; `@smthrs/engine-store` owns the mark policy and grace
|
|
18
|
+
* period.
|
|
19
|
+
*
|
|
20
|
+
* @since 1.0.0-rc.0
|
|
21
|
+
*/
|
|
22
|
+
import * as Context from "effect/Context";
|
|
23
|
+
import * as Effect from "effect/Effect";
|
|
24
|
+
import * as FileSystem from "effect/FileSystem";
|
|
25
|
+
import * as Layer from "effect/Layer";
|
|
26
|
+
import * as ArtifactStore from "./ArtifactStore.ts";
|
|
27
|
+
/**
|
|
28
|
+
* One enumerated blob: its content address, when it was last written or
|
|
29
|
+
* freshened, and its size.
|
|
30
|
+
*
|
|
31
|
+
* `modifiedAtMs` is the age evidence a sweep fences on — the same signal git
|
|
32
|
+
* (`git prune --expire`) and Bazel's disk-cache collector read, because it is
|
|
33
|
+
* the one timestamp a filesystem maintains without any bookkeeping of ours.
|
|
34
|
+
*
|
|
35
|
+
* @category models
|
|
36
|
+
* @since 1.0.0-rc.0
|
|
37
|
+
* @slop
|
|
38
|
+
*/
|
|
39
|
+
export interface BlobStat {
|
|
40
|
+
readonly digest: string;
|
|
41
|
+
readonly modifiedAtMs: number;
|
|
42
|
+
readonly sizeBytes: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Fencing predicate for a sweep deletion.
|
|
46
|
+
*
|
|
47
|
+
* @category models
|
|
48
|
+
* @since 1.0.0-rc.0
|
|
49
|
+
* @slop
|
|
50
|
+
*/
|
|
51
|
+
export interface RemoveOptions {
|
|
52
|
+
/**
|
|
53
|
+
* Deletes the blob only while its mtime is still at or before this bound.
|
|
54
|
+
* A blob freshened after the caller computed its live set — a concurrent
|
|
55
|
+
* `put` re-referencing the same bytes — fails the fence and survives, the
|
|
56
|
+
* same shape as `CacheStore.evict`'s `ifRecordedBy`: the guard rides in the
|
|
57
|
+
* delete, never in a prior read alone.
|
|
58
|
+
*/
|
|
59
|
+
readonly ifUnmodifiedSinceMs?: number | undefined;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Host-local blob enumeration and fenced deletion.
|
|
63
|
+
*
|
|
64
|
+
* @category models
|
|
65
|
+
* @since 1.0.0-rc.0
|
|
66
|
+
* @slop
|
|
67
|
+
*/
|
|
68
|
+
export interface Service {
|
|
69
|
+
/**
|
|
70
|
+
* Every content-addressed blob this host's store currently holds. In-flight
|
|
71
|
+
* `.tmp-*` scratch files, foreign files, and blobs whose age cannot be
|
|
72
|
+
* measured are excluded — what the sweep cannot judge it must not touch.
|
|
73
|
+
*/
|
|
74
|
+
readonly inventory: Effect.Effect<Array<BlobStat>, ArtifactStore.ArtifactStoreError>;
|
|
75
|
+
/**
|
|
76
|
+
* Deletes the blob at `digest`, returning whether bytes were removed. A
|
|
77
|
+
* missing blob reports `false` rather than failing, so a crashed and
|
|
78
|
+
* re-run sweep converges instead of erroring on its own progress.
|
|
79
|
+
*
|
|
80
|
+
* `false` covers three outcomes a caller cannot tell apart, all of which mean
|
|
81
|
+
* "nothing was reclaimed, and retrying later is safe": the blob was already
|
|
82
|
+
* gone, it failed the `ifUnmodifiedSinceMs` fence, or a live backup lease
|
|
83
|
+
* fenced the deletion (see `ArtifactBackupLease`). The last one is not
|
|
84
|
+
* progress — the blob is still there — so a collector that counts reclaimed
|
|
85
|
+
* bytes must not count it, and the next pass after the backup finishes
|
|
86
|
+
* removes it.
|
|
87
|
+
*/
|
|
88
|
+
readonly remove: (digest: string, options?: RemoveOptions) => Effect.Effect<boolean, ArtifactStore.ArtifactStoreError>;
|
|
89
|
+
}
|
|
90
|
+
declare const ArtifactSweep_base: Context.ServiceClass<ArtifactSweep, "@smthrs/artifacts/ArtifactSweep", Service>;
|
|
91
|
+
/**
|
|
92
|
+
* Service tag for host-local artifact sweeping.
|
|
93
|
+
*
|
|
94
|
+
* The identity string equals this module's package path, per the house rule
|
|
95
|
+
* that a new identity is the defining module path.
|
|
96
|
+
*
|
|
97
|
+
* @category services
|
|
98
|
+
* @since 1.0.0-rc.0
|
|
99
|
+
* @slop
|
|
100
|
+
*/
|
|
101
|
+
export declare class ArtifactSweep extends ArtifactSweep_base {
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* How the sweep reaches the objects directory.
|
|
105
|
+
*
|
|
106
|
+
* Deliberately not `ArtifactStore.FileSystemOptions`: the sweep never writes a
|
|
107
|
+
* blob, so `durability` would be an accepted argument it ignores. Both fields
|
|
108
|
+
* must match the store built over the same directory — see `coordination`.
|
|
109
|
+
*
|
|
110
|
+
* @category models
|
|
111
|
+
* @since 1.0.0-rc.0
|
|
112
|
+
*/
|
|
113
|
+
export interface SweepOptions {
|
|
114
|
+
/**
|
|
115
|
+
* The objects directory to enumerate, the one the paired
|
|
116
|
+
* `ArtifactStore.makeFileSystem` publishes into. Defaults to
|
|
117
|
+
* `ArtifactStore.defaultDirectory`.
|
|
118
|
+
*/
|
|
119
|
+
readonly directory?: string | undefined;
|
|
120
|
+
/**
|
|
121
|
+
* `required` takes the same per-digest lock file a publishing writer takes,
|
|
122
|
+
* and consults the backup lease, before deleting. It fences nothing unless
|
|
123
|
+
* the paired store was built with `required` too: a store on `process` never
|
|
124
|
+
* takes the lock this sweep waits on, so the fence would read as armed while
|
|
125
|
+
* protecting nothing. `process` is the explicit weaker browser/test mode.
|
|
126
|
+
*/
|
|
127
|
+
readonly coordination?: "required" | "process" | undefined;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Builds the filesystem-backed sweep surface over the same objects directory
|
|
131
|
+
* an `ArtifactStore.makeFileSystem` publishes into.
|
|
132
|
+
*
|
|
133
|
+
* Enumeration is conservative by construction. Only paths in the store's
|
|
134
|
+
* canonical fanout shape — `xx/<digest>` with `xx` equal to the digest's
|
|
135
|
+
* two-hex prefix — are blobs; anything else in the directory (a temp file, a
|
|
136
|
+
* lock file, a foreign file, a fanout directory itself) is skipped, never
|
|
137
|
+
* deleted. A blob that vanishes between listing and stat was removed by someone
|
|
138
|
+
* else and is likewise skipped.
|
|
139
|
+
*
|
|
140
|
+
* Pair it with the store over the SAME `directory` and the SAME `coordination`.
|
|
141
|
+
* Nothing checks the pairing, and a mismatch is silent: a sweep on `required`
|
|
142
|
+
* beside a store on `process` deletes blobs no writer is fenced against.
|
|
143
|
+
*
|
|
144
|
+
* Under `required`, one deletion costs two lock acquisitions — the per-digest
|
|
145
|
+
* lock, then the workspace-global backup-lease gate — which is roughly ten
|
|
146
|
+
* filesystem operations and two forked heartbeat fibers per blob. The gate is
|
|
147
|
+
* one file for the whole workspace, so concurrent deletions serialize through
|
|
148
|
+
* it. That is the price of never deleting a blob a running backup already
|
|
149
|
+
* recorded; size a collection window with it in mind.
|
|
150
|
+
*
|
|
151
|
+
* @category constructors
|
|
152
|
+
* @since 1.0.0-rc.0
|
|
153
|
+
* @slop
|
|
154
|
+
*/
|
|
155
|
+
export declare const makeFileSystem: (fs: FileSystem.FileSystem, options?: SweepOptions) => Service;
|
|
156
|
+
/**
|
|
157
|
+
* Provides the filesystem-backed sweep surface.
|
|
158
|
+
*
|
|
159
|
+
* Give it the same `directory` and `coordination` as the `ArtifactStore` layer
|
|
160
|
+
* it sweeps behind; see {@link makeFileSystem}.
|
|
161
|
+
*
|
|
162
|
+
* @category layers
|
|
163
|
+
* @since 1.0.0-rc.0
|
|
164
|
+
* @slop
|
|
165
|
+
*/
|
|
166
|
+
export declare const layerFileSystem: (options?: SweepOptions) => Layer.Layer<ArtifactSweep, never, FileSystem.FileSystem>;
|
|
167
|
+
/**
|
|
168
|
+
* Builds a sweep surface whose every operation fails as unavailable, with
|
|
169
|
+
* per-method overrides.
|
|
170
|
+
*
|
|
171
|
+
* @category constructors
|
|
172
|
+
* @since 1.0.0-rc.0
|
|
173
|
+
* @slop
|
|
174
|
+
*/
|
|
175
|
+
export declare const makeNoop: (overrides?: Partial<Service>) => Service;
|
|
176
|
+
/**
|
|
177
|
+
* Provides a no-op sweep surface.
|
|
178
|
+
*
|
|
179
|
+
* @category layers
|
|
180
|
+
* @since 1.0.0-rc.0
|
|
181
|
+
* @slop
|
|
182
|
+
*/
|
|
183
|
+
export declare const layerNoop: (overrides?: Partial<Service>) => Layer.Layer<ArtifactSweep>;
|
|
184
|
+
export {};
|
|
185
|
+
//# sourceMappingURL=ArtifactSweep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ArtifactSweep.d.ts","sourceRoot":"","sources":["../../src/ArtifactSweep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,UAAU,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAIrC,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAInD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAClD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,OAAO;IACtB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAA;IACpF;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,MAAM,EAAE,CACf,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,aAAa,KACpB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAA;CAC9D;;AAED;;;;;;;;;GASG;AACH,qBAAa,aAAc,SAAQ,kBAA4E;CAAG;AAelH;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,CAAA;CAC3D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,cAAc,GACzB,IAAI,UAAU,CAAC,UAAU,EACzB,UAAS,YAAiB,KACzB,OAiHF,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAC1B,UAAS,YAAiB,KACzB,KAAK,CAAC,KAAK,CAAC,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAC2C,CAAA;AAErG;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,GAAI,YAAW,OAAO,CAAC,OAAO,CAAM,KAAG,OAI1D,CAAA;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GAAI,YAAW,OAAO,CAAC,OAAO,CAAM,KAAG,KAAK,CAAC,KAAK,CAAC,aAAa,CACnC,CAAA"}
|