@telorun/kernel 0.42.0 → 0.43.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/dist/bundle/files-integrity.d.ts +25 -0
- package/dist/bundle/files-integrity.d.ts.map +1 -0
- package/dist/bundle/files-integrity.js +40 -0
- package/dist/bundle/files-integrity.js.map +1 -0
- package/dist/bundle/module-manifest.d.ts +22 -0
- package/dist/bundle/module-manifest.d.ts.map +1 -0
- package/dist/bundle/module-manifest.js +33 -0
- package/dist/bundle/module-manifest.js.map +1 -0
- package/dist/bundle/tar.d.ts +20 -0
- package/dist/bundle/tar.d.ts.map +1 -0
- package/dist/bundle/tar.js +63 -0
- package/dist/bundle/tar.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/kernel.d.ts.map +1 -1
- package/dist/kernel.js +8 -2
- package/dist/kernel.js.map +1 -1
- package/dist/manifest-sources/local-manifest-cache-source.d.ts +1 -1
- package/dist/manifest-sources/local-manifest-cache-source.d.ts.map +1 -1
- package/dist/manifest-sources/local-manifest-cache-source.js +14 -93
- package/dist/manifest-sources/local-manifest-cache-source.js.map +1 -1
- package/dist/transports/oci/docker-credentials.d.ts +10 -0
- package/dist/transports/oci/docker-credentials.d.ts.map +1 -0
- package/dist/transports/oci/docker-credentials.js +75 -0
- package/dist/transports/oci/docker-credentials.js.map +1 -0
- package/dist/transports/oci/oci-client.d.ts +51 -0
- package/dist/transports/oci/oci-client.d.ts.map +1 -0
- package/dist/transports/oci/oci-client.js +170 -0
- package/dist/transports/oci/oci-client.js.map +1 -0
- package/dist/transports/oci/oci-ref.d.ts +21 -0
- package/dist/transports/oci/oci-ref.d.ts.map +1 -0
- package/dist/transports/oci/oci-ref.js +36 -0
- package/dist/transports/oci/oci-ref.js.map +1 -0
- package/dist/transports/oci/oci-transport.d.ts +29 -0
- package/dist/transports/oci/oci-transport.d.ts.map +1 -0
- package/dist/transports/oci/oci-transport.js +142 -0
- package/dist/transports/oci/oci-transport.js.map +1 -0
- package/dist/transports/registry-transport.d.ts +23 -0
- package/dist/transports/registry-transport.d.ts.map +1 -0
- package/dist/transports/registry-transport.js +225 -0
- package/dist/transports/registry-transport.js.map +1 -0
- package/dist/transports/transport-registry.d.ts +40 -0
- package/dist/transports/transport-registry.d.ts.map +1 -0
- package/dist/transports/transport-registry.js +70 -0
- package/dist/transports/transport-registry.js.map +1 -0
- package/dist/transports/transport.d.ts +92 -0
- package/dist/transports/transport.d.ts.map +1 -0
- package/dist/transports/transport.js +2 -0
- package/dist/transports/transport.js.map +1 -0
- package/package.json +6 -3
- package/src/bundle/files-integrity.ts +46 -0
- package/src/bundle/module-manifest.ts +49 -0
- package/src/bundle/tar.ts +78 -0
- package/src/index.ts +22 -0
- package/src/kernel.ts +7 -2
- package/src/manifest-sources/local-manifest-cache-source.ts +14 -99
- package/src/transports/oci/docker-credentials.ts +85 -0
- package/src/transports/oci/oci-client.ts +237 -0
- package/src/transports/oci/oci-ref.ts +53 -0
- package/src/transports/oci/oci-transport.ts +192 -0
- package/src/transports/registry-transport.ts +281 -0
- package/src/transports/transport-registry.ts +91 -0
- package/src/transports/transport.ts +112 -0
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { splitIntegrity, verifyIntegrity } from "@telorun/analyzer";
|
|
2
|
-
import { createHash } from "crypto";
|
|
3
2
|
import { statSync } from "fs";
|
|
4
3
|
import * as fs from "fs/promises";
|
|
5
4
|
import * as path from "path";
|
|
6
5
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
7
6
|
import { hostEnv } from "../host-env.js";
|
|
7
|
+
import { defaultTransportRegistry } from "../transports/transport-registry.js";
|
|
8
8
|
const CACHE_SUBDIR = ".telo/manifests";
|
|
9
|
-
const HTTP_NAMESPACE = "__http";
|
|
10
|
-
const DEFAULT_MANIFEST_FILENAME = "telo.yaml";
|
|
11
9
|
const DEFAULT_REGISTRY_URL = "https://registry.telo.run";
|
|
12
|
-
const QUERY_HASH_LENGTH = 12;
|
|
13
10
|
/** Verify that `candidate` resolves to a path under `root`. Returns the
|
|
14
11
|
* candidate path on success, `null` when any segment escapes the root.
|
|
15
12
|
* Guards against `..` segments inside module refs or HTTP pathnames. */
|
|
@@ -24,95 +21,19 @@ function joinUnder(root, ...segments) {
|
|
|
24
21
|
}
|
|
25
22
|
return candidate;
|
|
26
23
|
}
|
|
27
|
-
/** Mirror `HttpSource.read`'s `fetchUrl` derivation: when the URL does not
|
|
28
|
-
* already point at a YAML file, append `/telo.yaml`. Used by both the
|
|
29
|
-
* reader and writer so a raw import URL and the canonical source it
|
|
30
|
-
* resolves to map to the same cache path. */
|
|
31
|
-
function normalizePathname(url, parsed) {
|
|
32
|
-
let pathname = parsed.pathname;
|
|
33
|
-
if (!url.includes(".yaml")) {
|
|
34
|
-
pathname = pathname.endsWith("/")
|
|
35
|
-
? `${pathname}${DEFAULT_MANIFEST_FILENAME}`
|
|
36
|
-
: `${pathname}/${DEFAULT_MANIFEST_FILENAME}`;
|
|
37
|
-
}
|
|
38
|
-
return pathname;
|
|
39
|
-
}
|
|
40
|
-
/** Compute a short hash of `search + hash` so two URLs that differ only in
|
|
41
|
-
* query / fragment do not collide at the same cache path. Inserted before
|
|
42
|
-
* the final extension so the filename stays readable. */
|
|
43
|
-
function disambiguatePath(pathname, search, hash) {
|
|
44
|
-
if (!search && !hash)
|
|
45
|
-
return pathname;
|
|
46
|
-
const digest = createHash("sha256")
|
|
47
|
-
.update(search + hash)
|
|
48
|
-
.digest("hex")
|
|
49
|
-
.slice(0, QUERY_HASH_LENGTH);
|
|
50
|
-
const ext = path.extname(pathname);
|
|
51
|
-
const base = pathname.slice(0, pathname.length - ext.length);
|
|
52
|
-
return `${base}.${digest}${ext}`;
|
|
53
|
-
}
|
|
54
24
|
/** Single source of truth for URL → cache path. Used identically by the
|
|
55
25
|
* reader (cache lookup) and writer (install-time persistence). For any
|
|
56
|
-
* given import
|
|
57
|
-
*
|
|
26
|
+
* given import ref — registry ref, direct registry URL, arbitrary HTTP, or
|
|
27
|
+
* (once registered) `oci://` — both sides land on the same file, because the
|
|
28
|
+
* owning transport decides the layout via {@link Transport.cacheLocation}.
|
|
58
29
|
*
|
|
59
|
-
* Returns `null` for unsupported
|
|
60
|
-
*
|
|
61
|
-
function cachePathForUrl(rawUrl, cacheRoot,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// 1. Registry ref form: namespace/name@version
|
|
67
|
-
if (!url.startsWith("http://") &&
|
|
68
|
-
!url.startsWith("https://") &&
|
|
69
|
-
!url.startsWith("/") &&
|
|
70
|
-
!url.startsWith(".") &&
|
|
71
|
-
!url.startsWith("file://") &&
|
|
72
|
-
!url.startsWith("memory://") &&
|
|
73
|
-
url.includes("@") &&
|
|
74
|
-
url.includes("/")) {
|
|
75
|
-
const atIdx = url.lastIndexOf("@");
|
|
76
|
-
if (atIdx <= 0 || atIdx === url.length - 1)
|
|
77
|
-
return null;
|
|
78
|
-
const modulePath = url.slice(0, atIdx);
|
|
79
|
-
if (!modulePath.includes("/"))
|
|
80
|
-
return null;
|
|
81
|
-
const version = url.slice(atIdx + 1).replace(/^v/, "");
|
|
82
|
-
if (!version)
|
|
83
|
-
return null;
|
|
84
|
-
return joinUnder(cacheRoot, modulePath, version, DEFAULT_MANIFEST_FILENAME);
|
|
85
|
-
}
|
|
86
|
-
// 2. HTTP(S) URL — could be a direct registry URL or arbitrary external.
|
|
87
|
-
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
88
|
-
let parsed;
|
|
89
|
-
try {
|
|
90
|
-
parsed = new URL(url);
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
const pathname = normalizePathname(url, parsed);
|
|
96
|
-
// 2a. URL is on the configured registry, with no query/fragment:
|
|
97
|
-
// fold into the registry layout so the writer that received a
|
|
98
|
-
// registry-ref canonical and the reader that sees a direct URL
|
|
99
|
-
// both resolve to the same file.
|
|
100
|
-
const normalizedUrl = `${parsed.protocol}//${parsed.host}${pathname}`;
|
|
101
|
-
if (!parsed.search &&
|
|
102
|
-
!parsed.hash &&
|
|
103
|
-
(normalizedUrl === trimmedRegistry || normalizedUrl.startsWith(`${trimmedRegistry}/`))) {
|
|
104
|
-
const rel = normalizedUrl.slice(trimmedRegistry.length + 1);
|
|
105
|
-
if (!rel)
|
|
106
|
-
return null;
|
|
107
|
-
return joinUnder(cacheRoot, ...rel.split("/"));
|
|
108
|
-
}
|
|
109
|
-
// 2b. Arbitrary HTTP(S) URL → __http subtree, with a short query-hash
|
|
110
|
-
// suffix when query / fragment are present to prevent collisions.
|
|
111
|
-
const cleanPath = pathname.startsWith("/") ? pathname.slice(1) : pathname;
|
|
112
|
-
const disambiguated = disambiguatePath(cleanPath, parsed.search, parsed.hash);
|
|
113
|
-
return joinUnder(cacheRoot, HTTP_NAMESPACE, parsed.host, ...disambiguated.split("/"));
|
|
114
|
-
}
|
|
115
|
-
return null;
|
|
30
|
+
* Returns `null` for unsupported refs (file://, memory://, relative paths) or
|
|
31
|
+
* for path-traversal attempts that would escape `cacheRoot`. */
|
|
32
|
+
function cachePathForUrl(rawUrl, cacheRoot, transports) {
|
|
33
|
+
const segments = transports.cacheLocation(rawUrl);
|
|
34
|
+
if (!segments)
|
|
35
|
+
return null;
|
|
36
|
+
return joinUnder(cacheRoot, ...segments);
|
|
116
37
|
}
|
|
117
38
|
/**
|
|
118
39
|
* Reads previously-cached manifest YAMLs from `<entry-dir>/.telo/manifests/`.
|
|
@@ -127,7 +48,7 @@ export class LocalManifestCacheSource {
|
|
|
127
48
|
// single `resolveCacheRoot` (honours `TELO_CACHE_DIR`); when absent we fall
|
|
128
49
|
// back to the entry-anchored default so library/test callers are unchanged.
|
|
129
50
|
this.cacheRoot = manifestsDir ?? path.join(entryDir, CACHE_SUBDIR);
|
|
130
|
-
this.
|
|
51
|
+
this.transports = defaultTransportRegistry(registryUrl);
|
|
131
52
|
}
|
|
132
53
|
supports(url) {
|
|
133
54
|
return this.tryMap(url) !== null;
|
|
@@ -158,7 +79,7 @@ export class LocalManifestCacheSource {
|
|
|
158
79
|
return new URL(relative, baseDir).href;
|
|
159
80
|
}
|
|
160
81
|
tryMap(url) {
|
|
161
|
-
const candidate = cachePathForUrl(url, this.cacheRoot, this.
|
|
82
|
+
const candidate = cachePathForUrl(url, this.cacheRoot, this.transports);
|
|
162
83
|
if (!candidate)
|
|
163
84
|
return null;
|
|
164
85
|
// Require a regular file. A directory, dangling symlink, or stat failure
|
|
@@ -183,7 +104,7 @@ export class LocalManifestCacheSource {
|
|
|
183
104
|
*/
|
|
184
105
|
export function cachePathForCanonical(canonicalSource, entryDir, registryUrl, manifestsDir) {
|
|
185
106
|
const cacheRoot = manifestsDir ?? path.join(entryDir, CACHE_SUBDIR);
|
|
186
|
-
return cachePathForUrl(canonicalSource, cacheRoot, registryUrl);
|
|
107
|
+
return cachePathForUrl(canonicalSource, cacheRoot, defaultTransportRegistry(registryUrl));
|
|
187
108
|
}
|
|
188
109
|
/**
|
|
189
110
|
* Persist every manifest file reachable from `graph` (owners + partials) to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-manifest-cache-source.js","sourceRoot":"","sources":["../../src/manifest-sources/local-manifest-cache-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAyC,MAAM,mBAAmB,CAAC;AAC3G,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"local-manifest-cache-source.js","sourceRoot":"","sources":["../../src/manifest-sources/local-manifest-cache-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface DockerCredential {
|
|
2
|
+
username: string;
|
|
3
|
+
password: string;
|
|
4
|
+
}
|
|
5
|
+
/** Resolve credentials for `host` from the ambient Docker credential chain:
|
|
6
|
+
* a per-registry credential helper, the global credential store, then the
|
|
7
|
+
* inline `auths` entry (base64 `user:password` or explicit fields). Returns
|
|
8
|
+
* null for anonymous access — the caller falls back to an anonymous token. */
|
|
9
|
+
export declare function resolveDockerCredential(host: string): Promise<DockerCredential | null>;
|
|
10
|
+
//# sourceMappingURL=docker-credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-credentials.d.ts","sourceRoot":"","sources":["../../../src/transports/oci/docker-credentials.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAkDD;;;+EAG+E;AAC/E,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAoB5F"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { hostEnv } from "../../host-env.js";
|
|
6
|
+
/** Path to `config.json`, honouring `DOCKER_CONFIG`. */
|
|
7
|
+
function dockerConfigPath() {
|
|
8
|
+
const override = hostEnv().DOCKER_CONFIG;
|
|
9
|
+
const dir = override && override.trim() ? override.trim() : path.join(homedir(), ".docker");
|
|
10
|
+
return path.join(dir, "config.json");
|
|
11
|
+
}
|
|
12
|
+
async function readDockerConfig() {
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(await readFile(dockerConfigPath(), "utf-8"));
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null; // no config, unreadable, or malformed → anonymous
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Run `docker-credential-<helper> get` with `host` on stdin, per the Docker
|
|
21
|
+
* credential-helper protocol, returning `{Username, Secret}` or null. */
|
|
22
|
+
function runCredentialHelper(helper, host) {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
const proc = spawn(`docker-credential-${helper}`, ["get"], {
|
|
25
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
26
|
+
});
|
|
27
|
+
let out = "";
|
|
28
|
+
proc.stdout.on("data", (c) => (out += c));
|
|
29
|
+
proc.on("error", () => resolve(null)); // helper binary not installed
|
|
30
|
+
proc.on("close", (code) => {
|
|
31
|
+
if (code !== 0)
|
|
32
|
+
return resolve(null);
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(out);
|
|
35
|
+
if (parsed.Username && parsed.Secret) {
|
|
36
|
+
resolve({ username: parsed.Username, password: parsed.Secret });
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
resolve(null);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
resolve(null);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
proc.stdin.end(host);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/** Resolve credentials for `host` from the ambient Docker credential chain:
|
|
50
|
+
* a per-registry credential helper, the global credential store, then the
|
|
51
|
+
* inline `auths` entry (base64 `user:password` or explicit fields). Returns
|
|
52
|
+
* null for anonymous access — the caller falls back to an anonymous token. */
|
|
53
|
+
export async function resolveDockerCredential(host) {
|
|
54
|
+
const config = await readDockerConfig();
|
|
55
|
+
if (!config)
|
|
56
|
+
return null;
|
|
57
|
+
const helper = config.credHelpers?.[host];
|
|
58
|
+
if (helper)
|
|
59
|
+
return runCredentialHelper(helper, host);
|
|
60
|
+
if (config.credsStore)
|
|
61
|
+
return runCredentialHelper(config.credsStore, host);
|
|
62
|
+
const entry = config.auths?.[host];
|
|
63
|
+
if (entry?.auth) {
|
|
64
|
+
const decoded = Buffer.from(entry.auth, "base64").toString("utf-8");
|
|
65
|
+
const colon = decoded.indexOf(":");
|
|
66
|
+
if (colon > 0) {
|
|
67
|
+
return { username: decoded.slice(0, colon), password: decoded.slice(colon + 1) };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (entry?.username && entry?.password) {
|
|
71
|
+
return { username: entry.username, password: entry.password };
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=docker-credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-credentials.js","sourceRoot":"","sources":["../../../src/transports/oci/docker-credentials.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export declare const TELO_LAYER_MEDIA_TYPE = "application/vnd.telo.module.v1+tar";
|
|
2
|
+
export declare const OCI_MANIFEST_MEDIA_TYPE = "application/vnd.oci.image.manifest.v1+json";
|
|
3
|
+
export declare const OCI_EMPTY_CONFIG_MEDIA_TYPE = "application/vnd.oci.empty.v1+json";
|
|
4
|
+
export interface OciDescriptor {
|
|
5
|
+
mediaType: string;
|
|
6
|
+
digest: string;
|
|
7
|
+
size: number;
|
|
8
|
+
data?: string;
|
|
9
|
+
artifactType?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface OciManifest {
|
|
12
|
+
schemaVersion: number;
|
|
13
|
+
mediaType?: string;
|
|
14
|
+
artifactType?: string;
|
|
15
|
+
config: OciDescriptor;
|
|
16
|
+
layers: OciDescriptor[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A minimal OCI distribution (registry v2) client — exactly the surface Telo
|
|
20
|
+
* needs: pull manifest / pull blob / push blob / push manifest / list tags,
|
|
21
|
+
* with the `WWW-Authenticate` bearer-token handshake and the ambient Docker
|
|
22
|
+
* credential chain. Hand-rolled rather than depending on a stale/Deno-only
|
|
23
|
+
* client (see plan Decisions). One instance per `(host, repo)`; tokens are
|
|
24
|
+
* cached per scope for the instance's lifetime.
|
|
25
|
+
*/
|
|
26
|
+
export declare class OciClient {
|
|
27
|
+
private readonly host;
|
|
28
|
+
private readonly repo;
|
|
29
|
+
private readonly tokenByScope;
|
|
30
|
+
constructor(host: string, repo: string);
|
|
31
|
+
private base;
|
|
32
|
+
private pullScope;
|
|
33
|
+
private pushScope;
|
|
34
|
+
/** Fetch with the bearer-token dance: try (cached token if any), and on 401
|
|
35
|
+
* resolve a token from the `WWW-Authenticate` challenge and retry once. */
|
|
36
|
+
private authedFetch;
|
|
37
|
+
/** Exchange a bearer challenge for a token, authenticating to the token
|
|
38
|
+
* service with Docker credentials when available (else anonymous). */
|
|
39
|
+
private fetchToken;
|
|
40
|
+
pullManifest(reference: string): Promise<OciManifest>;
|
|
41
|
+
pullBlob(digest: string): Promise<Buffer>;
|
|
42
|
+
listTags(): Promise<string[]>;
|
|
43
|
+
/** Upload `bytes` as a blob (skipped when already present), returning its
|
|
44
|
+
* `sha256:<hex>` digest. Two-step upload: obtain a session, then PUT with
|
|
45
|
+
* the digest. */
|
|
46
|
+
pushBlob(bytes: Uint8Array): Promise<string>;
|
|
47
|
+
pushManifest(reference: string, manifest: OciManifest): Promise<void>;
|
|
48
|
+
/** Push the standard empty config blob and return its descriptor. */
|
|
49
|
+
pushEmptyConfig(): Promise<OciDescriptor>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=oci-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oci-client.d.ts","sourceRoot":"","sources":["../../../src/transports/oci/oci-client.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,uCAAuC,CAAC;AAC1E,eAAO,MAAM,uBAAuB,+CAA+C,CAAC;AACpF,eAAO,MAAM,2BAA2B,sCAAsC,CAAC;AAa/E,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAgBD;;;;;;;GAOG;AACH,qBAAa,SAAS;IAIlB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJvB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;gBAGvC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM;IAG/B,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,SAAS;IAIjB;gFAC4E;YAC9D,WAAW;IAyBzB;2EACuE;YACzD,UAAU;IAmBlB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAcrD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUzC,QAAQ,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAYnC;;sBAEkB;IACZ,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IA+C5C,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAe3E,qEAAqE;IAC/D,eAAe,IAAI,OAAO,CAAC,aAAa,CAAC;CAShD"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { resolveDockerCredential } from "./docker-credentials.js";
|
|
3
|
+
export const TELO_LAYER_MEDIA_TYPE = "application/vnd.telo.module.v1+tar";
|
|
4
|
+
export const OCI_MANIFEST_MEDIA_TYPE = "application/vnd.oci.image.manifest.v1+json";
|
|
5
|
+
export const OCI_EMPTY_CONFIG_MEDIA_TYPE = "application/vnd.oci.empty.v1+json";
|
|
6
|
+
/** The standard OCI empty descriptor — config `{}` (2 bytes). */
|
|
7
|
+
const EMPTY_CONFIG = Buffer.from("{}", "utf-8");
|
|
8
|
+
const EMPTY_CONFIG_DIGEST = "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a";
|
|
9
|
+
const MANIFEST_ACCEPT = [
|
|
10
|
+
OCI_MANIFEST_MEDIA_TYPE,
|
|
11
|
+
"application/vnd.oci.image.index.v1+json",
|
|
12
|
+
"application/vnd.docker.distribution.manifest.v2+json",
|
|
13
|
+
"application/vnd.docker.distribution.manifest.list.v2+json",
|
|
14
|
+
].join(", ");
|
|
15
|
+
function sha256Hex(bytes) {
|
|
16
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
17
|
+
}
|
|
18
|
+
/** Parse a `WWW-Authenticate: Bearer realm="...",service="...",scope="..."` header. */
|
|
19
|
+
function parseBearerChallenge(header) {
|
|
20
|
+
const out = {};
|
|
21
|
+
const params = header.replace(/^Bearer\s+/i, "");
|
|
22
|
+
for (const m of params.matchAll(/([a-z]+)="([^"]*)"/gi)) {
|
|
23
|
+
out[m[1].toLowerCase()] = m[2];
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A minimal OCI distribution (registry v2) client — exactly the surface Telo
|
|
29
|
+
* needs: pull manifest / pull blob / push blob / push manifest / list tags,
|
|
30
|
+
* with the `WWW-Authenticate` bearer-token handshake and the ambient Docker
|
|
31
|
+
* credential chain. Hand-rolled rather than depending on a stale/Deno-only
|
|
32
|
+
* client (see plan Decisions). One instance per `(host, repo)`; tokens are
|
|
33
|
+
* cached per scope for the instance's lifetime.
|
|
34
|
+
*/
|
|
35
|
+
export class OciClient {
|
|
36
|
+
constructor(host, repo) {
|
|
37
|
+
this.host = host;
|
|
38
|
+
this.repo = repo;
|
|
39
|
+
this.tokenByScope = new Map();
|
|
40
|
+
}
|
|
41
|
+
base() {
|
|
42
|
+
return `https://${this.host}/v2/${this.repo}`;
|
|
43
|
+
}
|
|
44
|
+
pullScope() {
|
|
45
|
+
return `repository:${this.repo}:pull`;
|
|
46
|
+
}
|
|
47
|
+
pushScope() {
|
|
48
|
+
return `repository:${this.repo}:pull,push`;
|
|
49
|
+
}
|
|
50
|
+
/** Fetch with the bearer-token dance: try (cached token if any), and on 401
|
|
51
|
+
* resolve a token from the `WWW-Authenticate` challenge and retry once. */
|
|
52
|
+
async authedFetch(url, init, scope) {
|
|
53
|
+
const withToken = (token) => {
|
|
54
|
+
const headers = new Headers(init.headers);
|
|
55
|
+
if (token)
|
|
56
|
+
headers.set("authorization", `Bearer ${token}`);
|
|
57
|
+
return { ...init, headers };
|
|
58
|
+
};
|
|
59
|
+
const cached = this.tokenByScope.get(scope);
|
|
60
|
+
let res = await fetch(url, withToken(cached));
|
|
61
|
+
if (res.status !== 401)
|
|
62
|
+
return res;
|
|
63
|
+
const challenge = res.headers.get("www-authenticate");
|
|
64
|
+
if (!challenge)
|
|
65
|
+
return res;
|
|
66
|
+
const token = await this.fetchToken(challenge, scope);
|
|
67
|
+
if (!token)
|
|
68
|
+
return res;
|
|
69
|
+
this.tokenByScope.set(scope, token);
|
|
70
|
+
// Drain the 401 body so the connection can be reused.
|
|
71
|
+
await res.text().catch(() => { });
|
|
72
|
+
return fetch(url, withToken(token));
|
|
73
|
+
}
|
|
74
|
+
/** Exchange a bearer challenge for a token, authenticating to the token
|
|
75
|
+
* service with Docker credentials when available (else anonymous). */
|
|
76
|
+
async fetchToken(challenge, scope) {
|
|
77
|
+
const params = parseBearerChallenge(challenge);
|
|
78
|
+
if (!params.realm)
|
|
79
|
+
return null;
|
|
80
|
+
const tokenUrl = new URL(params.realm);
|
|
81
|
+
if (params.service)
|
|
82
|
+
tokenUrl.searchParams.set("service", params.service);
|
|
83
|
+
tokenUrl.searchParams.set("scope", params.scope || scope);
|
|
84
|
+
const headers = new Headers();
|
|
85
|
+
const cred = await resolveDockerCredential(this.host);
|
|
86
|
+
if (cred) {
|
|
87
|
+
const basic = Buffer.from(`${cred.username}:${cred.password}`).toString("base64");
|
|
88
|
+
headers.set("authorization", `Basic ${basic}`);
|
|
89
|
+
}
|
|
90
|
+
const res = await fetch(tokenUrl.href, { headers });
|
|
91
|
+
if (!res.ok)
|
|
92
|
+
return null;
|
|
93
|
+
const body = (await res.json());
|
|
94
|
+
return body.token ?? body.access_token ?? null;
|
|
95
|
+
}
|
|
96
|
+
async pullManifest(reference) {
|
|
97
|
+
const res = await this.authedFetch(`${this.base()}/manifests/${reference}`, { headers: { accept: MANIFEST_ACCEPT } }, this.pullScope());
|
|
98
|
+
if (!res.ok) {
|
|
99
|
+
throw new Error(`OCI pull manifest ${this.repo}:${reference} on ${this.host} failed: ${res.status} ${res.statusText}`);
|
|
100
|
+
}
|
|
101
|
+
return (await res.json());
|
|
102
|
+
}
|
|
103
|
+
async pullBlob(digest) {
|
|
104
|
+
const res = await this.authedFetch(`${this.base()}/blobs/${digest}`, {}, this.pullScope());
|
|
105
|
+
if (!res.ok) {
|
|
106
|
+
throw new Error(`OCI pull blob ${digest} from ${this.repo} on ${this.host} failed: ${res.status} ${res.statusText}`);
|
|
107
|
+
}
|
|
108
|
+
return Buffer.from(await res.arrayBuffer());
|
|
109
|
+
}
|
|
110
|
+
async listTags() {
|
|
111
|
+
const res = await this.authedFetch(`${this.base()}/tags/list`, {}, this.pullScope());
|
|
112
|
+
if (res.status === 404)
|
|
113
|
+
return [];
|
|
114
|
+
if (!res.ok) {
|
|
115
|
+
throw new Error(`OCI list tags for ${this.repo} on ${this.host} failed: ${res.status} ${res.statusText}`);
|
|
116
|
+
}
|
|
117
|
+
const body = (await res.json());
|
|
118
|
+
return Array.isArray(body.tags) ? body.tags : [];
|
|
119
|
+
}
|
|
120
|
+
/** Upload `bytes` as a blob (skipped when already present), returning its
|
|
121
|
+
* `sha256:<hex>` digest. Two-step upload: obtain a session, then PUT with
|
|
122
|
+
* the digest. */
|
|
123
|
+
async pushBlob(bytes) {
|
|
124
|
+
const digest = `sha256:${sha256Hex(bytes)}`;
|
|
125
|
+
const head = await this.authedFetch(`${this.base()}/blobs/${digest}`, { method: "HEAD" }, this.pushScope());
|
|
126
|
+
if (head.ok)
|
|
127
|
+
return digest; // already present
|
|
128
|
+
await head.text().catch(() => { });
|
|
129
|
+
const start = await this.authedFetch(`${this.base()}/blobs/uploads/`, { method: "POST" }, this.pushScope());
|
|
130
|
+
if (start.status !== 202) {
|
|
131
|
+
throw new Error(`OCI blob upload start for ${this.repo} on ${this.host} failed: ${start.status} ${start.statusText}`);
|
|
132
|
+
}
|
|
133
|
+
const location = start.headers.get("location");
|
|
134
|
+
await start.text().catch(() => { });
|
|
135
|
+
if (!location) {
|
|
136
|
+
throw new Error(`OCI blob upload for ${this.repo} returned no Location header`);
|
|
137
|
+
}
|
|
138
|
+
const uploadUrl = new URL(location, `https://${this.host}`);
|
|
139
|
+
uploadUrl.searchParams.set("digest", digest);
|
|
140
|
+
const put = await this.authedFetch(uploadUrl.href, {
|
|
141
|
+
method: "PUT",
|
|
142
|
+
headers: { "content-type": "application/octet-stream" },
|
|
143
|
+
body: bytes,
|
|
144
|
+
}, this.pushScope());
|
|
145
|
+
if (put.status !== 201) {
|
|
146
|
+
const detail = await put.text().catch(() => "");
|
|
147
|
+
throw new Error(`OCI blob PUT for ${this.repo} on ${this.host} failed: ${put.status} ${put.statusText} ${detail}`);
|
|
148
|
+
}
|
|
149
|
+
return digest;
|
|
150
|
+
}
|
|
151
|
+
async pushManifest(reference, manifest) {
|
|
152
|
+
const body = Buffer.from(JSON.stringify(manifest), "utf-8");
|
|
153
|
+
const res = await this.authedFetch(`${this.base()}/manifests/${reference}`, { method: "PUT", headers: { "content-type": OCI_MANIFEST_MEDIA_TYPE }, body }, this.pushScope());
|
|
154
|
+
if (res.status !== 201) {
|
|
155
|
+
const detail = await res.text().catch(() => "");
|
|
156
|
+
throw new Error(`OCI push manifest ${this.repo}:${reference} on ${this.host} failed: ${res.status} ${res.statusText} ${detail}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** Push the standard empty config blob and return its descriptor. */
|
|
160
|
+
async pushEmptyConfig() {
|
|
161
|
+
await this.pushBlob(EMPTY_CONFIG);
|
|
162
|
+
return {
|
|
163
|
+
mediaType: OCI_EMPTY_CONFIG_MEDIA_TYPE,
|
|
164
|
+
digest: EMPTY_CONFIG_DIGEST,
|
|
165
|
+
size: EMPTY_CONFIG.length,
|
|
166
|
+
data: EMPTY_CONFIG.toString("base64"),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=oci-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oci-client.js","sourceRoot":"","sources":["../../../src/transports/oci/oci-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const OCI_SCHEME = "oci://";
|
|
2
|
+
/** A parsed `oci://host/repo@reference` module ref.
|
|
3
|
+
*
|
|
4
|
+
* - `host` — the registry host (`ghcr.io`, `123.dkr.ecr.us-east-1.amazonaws.com`).
|
|
5
|
+
* - `repo` — the repository path (`aws/telo-s3`), possibly multi-segment.
|
|
6
|
+
* - `reference` — a tag (`1.2.0`) or a digest (`sha256:...`); the OCI address.
|
|
7
|
+
* - `integrity` — Telo's inline `sha256-<base64url>` hash when the ref is pinned
|
|
8
|
+
* (authoritative across transports; the OCI digest is only corroborating). */
|
|
9
|
+
export interface ParsedOciRef {
|
|
10
|
+
host: string;
|
|
11
|
+
repo: string;
|
|
12
|
+
reference: string;
|
|
13
|
+
integrity?: string;
|
|
14
|
+
}
|
|
15
|
+
/** True when `ref` uses the `oci://` scheme (integrity fragment tolerated). */
|
|
16
|
+
export declare function isOciRef(ref: string): boolean;
|
|
17
|
+
/** Parse `oci://host/repo@reference[#sha256-...]`. Throws on a malformed ref.
|
|
18
|
+
* A `reference` may be a tag or a `sha256:` digest; when absent (`@` omitted)
|
|
19
|
+
* it defaults to `latest`, matching OCI tooling. */
|
|
20
|
+
export declare function parseOciRef(ref: string): ParsedOciRef;
|
|
21
|
+
//# sourceMappingURL=oci-ref.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oci-ref.d.ts","sourceRoot":"","sources":["../../../src/transports/oci/oci-ref.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU,WAAW,CAAC;AAEnC;;;;;;iFAMiF;AACjF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,+EAA+E;AAC/E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED;;qDAEqD;AACrD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CA0BrD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { splitIntegrity } from "@telorun/analyzer";
|
|
2
|
+
export const OCI_SCHEME = "oci://";
|
|
3
|
+
/** True when `ref` uses the `oci://` scheme (integrity fragment tolerated). */
|
|
4
|
+
export function isOciRef(ref) {
|
|
5
|
+
return splitIntegrity(ref).base.startsWith(OCI_SCHEME);
|
|
6
|
+
}
|
|
7
|
+
/** Parse `oci://host/repo@reference[#sha256-...]`. Throws on a malformed ref.
|
|
8
|
+
* A `reference` may be a tag or a `sha256:` digest; when absent (`@` omitted)
|
|
9
|
+
* it defaults to `latest`, matching OCI tooling. */
|
|
10
|
+
export function parseOciRef(ref) {
|
|
11
|
+
const { base, integrity } = splitIntegrity(ref);
|
|
12
|
+
if (!base.startsWith(OCI_SCHEME)) {
|
|
13
|
+
throw new Error(`Invalid OCI reference '${ref}', expected oci://host/repo@reference`);
|
|
14
|
+
}
|
|
15
|
+
const rest = base.slice(OCI_SCHEME.length);
|
|
16
|
+
const slash = rest.indexOf("/");
|
|
17
|
+
if (slash <= 0) {
|
|
18
|
+
throw new Error(`Invalid OCI reference '${ref}', missing repository path after host`);
|
|
19
|
+
}
|
|
20
|
+
const host = rest.slice(0, slash);
|
|
21
|
+
let repoAndRef = rest.slice(slash + 1);
|
|
22
|
+
let reference = "latest";
|
|
23
|
+
const at = repoAndRef.lastIndexOf("@");
|
|
24
|
+
// A digest reference is `repo@sha256:...` — the `@` before `sha256:` is the
|
|
25
|
+
// separator, not part of the repo. A tag reference has no `@`.
|
|
26
|
+
if (at > 0) {
|
|
27
|
+
reference = repoAndRef.slice(at + 1);
|
|
28
|
+
repoAndRef = repoAndRef.slice(0, at);
|
|
29
|
+
}
|
|
30
|
+
const repo = repoAndRef;
|
|
31
|
+
if (!host || !repo || !reference) {
|
|
32
|
+
throw new Error(`Invalid OCI reference '${ref}', expected oci://host/repo@reference`);
|
|
33
|
+
}
|
|
34
|
+
return { host, repo, reference, integrity };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=oci-ref.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oci-ref.js","sourceRoot":"","sources":["../../../src/transports/oci/oci-ref.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC;AAgBnC,+EAA+E;AAC/E,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AACzD,CAAC;AAED;;qDAEqD;AACrD,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,uCAAuC,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,uCAAuC,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAClC,IAAI,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAEvC,IAAI,SAAS,GAAG,QAAQ,CAAC;IACzB,MAAM,EAAE,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACvC,4EAA4E;IAC5E,+DAA+D;IAC/D,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QACX,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACrC,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACvC,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC;IACxB,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,uCAAuC,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ManifestSource } from "@telorun/analyzer";
|
|
2
|
+
import type { FetchedArtifact, PublishBundle, PublishOptions, PublishResult, SiblingIdentity, Transport } from "../transport.js";
|
|
3
|
+
/**
|
|
4
|
+
* OCI transport: `oci://host/repo@reference` modules on any OCI distribution
|
|
5
|
+
* registry (GHCR / ECR / Docker Hub / Harbor), over a hand-rolled minimal
|
|
6
|
+
* client. A module is a single artifact — one tar blob carrying `telo.yaml`
|
|
7
|
+
* and the `files:` payload — pushed under a standard OCI artifact manifest.
|
|
8
|
+
*
|
|
9
|
+
* Not browser-reachable (token handshake, Docker credentials, tar extraction),
|
|
10
|
+
* so its resolution `source` is Node-only; the editor resolves `oci://` imports
|
|
11
|
+
* through the discovery hub instead.
|
|
12
|
+
*/
|
|
13
|
+
export declare class OciTransport implements Transport {
|
|
14
|
+
readonly source: ManifestSource;
|
|
15
|
+
constructor();
|
|
16
|
+
supports(ref: string): boolean;
|
|
17
|
+
cacheLocation(ref: string): string[] | null;
|
|
18
|
+
/** Resolve a relative import against an `oci://` base, normalizing the repo to
|
|
19
|
+
* a directory base so `../lib` under `oci://ghcr.io/aws/my-app` →
|
|
20
|
+
* `oci://ghcr.io/aws/lib` (never `oci://ghcr.io/lib`). Non-relative refs pass
|
|
21
|
+
* through. The reference/tag is dropped — the caller re-pins from the
|
|
22
|
+
* sibling's own version. */
|
|
23
|
+
resolveRelative(base: string, relative: string): string;
|
|
24
|
+
listVersions(ref: string): Promise<string[] | null>;
|
|
25
|
+
fetchArtifact(ref: string): Promise<FetchedArtifact>;
|
|
26
|
+
publish(destination: string, bundle: PublishBundle, _opts?: PublishOptions): Promise<PublishResult>;
|
|
27
|
+
canonicalizeSiblingRef(destination: string, relativeSource: string, sibling: SiblingIdentity): string;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=oci-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oci-transport.d.ts","sourceRoot":"","sources":["../../../src/transports/oci/oci-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,KAAK,EACV,eAAe,EACf,aAAa,EACb,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACV,MAAM,iBAAiB,CAAC;AAmDzB;;;;;;;;;GASG;AACH,qBAAa,YAAa,YAAW,SAAS;IAC5C,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;;IAchC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI9B,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAU3C;;;;iCAI6B;IAC7B,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IASjD,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC;IAMnD,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAIpD,OAAO,CACX,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,aAAa,EACrB,KAAK,GAAE,cAAmB,GACzB,OAAO,CAAC,aAAa,CAAC;IA+CzB,sBAAsB,CACpB,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,eAAe,GACvB,MAAM;CAKV"}
|