@shard-for-obsidian/lib 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/README.md +77 -0
- package/dist/client/FetchAdapter.d.ts +14 -0
- package/dist/client/FetchAdapter.d.ts.map +1 -0
- package/dist/client/FetchAdapter.js +1 -0
- package/dist/client/OciRegistryClient.d.ts +196 -0
- package/dist/client/OciRegistryClient.d.ts.map +1 -0
- package/dist/client/OciRegistryClient.js +704 -0
- package/dist/client/RegistryClientOptions.d.ts +18 -0
- package/dist/client/RegistryClientOptions.d.ts.map +1 -0
- package/dist/client/RegistryClientOptions.js +1 -0
- package/dist/errors/RegistryErrors.d.ts +39 -0
- package/dist/errors/RegistryErrors.d.ts.map +1 -0
- package/dist/errors/RegistryErrors.js +52 -0
- package/dist/ghcr/GhcrConstants.d.ts +5 -0
- package/dist/ghcr/GhcrConstants.d.ts.map +1 -0
- package/dist/ghcr/GhcrConstants.js +4 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +7 -0
- package/dist/parsing/IndexParser.d.ts +26 -0
- package/dist/parsing/IndexParser.d.ts.map +1 -0
- package/dist/parsing/IndexParser.js +106 -0
- package/dist/parsing/LinkHeaderParser.d.ts +8 -0
- package/dist/parsing/LinkHeaderParser.d.ts.map +1 -0
- package/dist/parsing/LinkHeaderParser.js +34 -0
- package/dist/parsing/RepoParser.d.ts +54 -0
- package/dist/parsing/RepoParser.d.ts.map +1 -0
- package/dist/parsing/RepoParser.js +186 -0
- package/dist/types/AuthTypes.d.ts +14 -0
- package/dist/types/AuthTypes.d.ts.map +1 -0
- package/dist/types/AuthTypes.js +4 -0
- package/dist/types/ManifestTypes.d.ts +98 -0
- package/dist/types/ManifestTypes.d.ts.map +1 -0
- package/dist/types/ManifestTypes.js +7 -0
- package/dist/types/RegistryTypes.d.ts +48 -0
- package/dist/types/RegistryTypes.d.ts.map +1 -0
- package/dist/types/RegistryTypes.js +4 -0
- package/dist/types/RequestTypes.d.ts +23 -0
- package/dist/types/RequestTypes.d.ts.map +1 -0
- package/dist/types/RequestTypes.js +4 -0
- package/dist/utils/DigestUtils.d.ts +9 -0
- package/dist/utils/DigestUtils.d.ts.map +1 -0
- package/dist/utils/DigestUtils.js +26 -0
- package/dist/utils/ValidationUtils.d.ts +2 -0
- package/dist/utils/ValidationUtils.d.ts.map +1 -0
- package/dist/utils/ValidationUtils.js +6 -0
- package/package.json +50 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RegistryRepo } from "../types/RegistryTypes.js";
|
|
2
|
+
export interface RegistryClientOptions {
|
|
3
|
+
name?: string;
|
|
4
|
+
repo?: RegistryRepo;
|
|
5
|
+
username?: string;
|
|
6
|
+
password?: string;
|
|
7
|
+
token?: string;
|
|
8
|
+
insecure?: boolean;
|
|
9
|
+
scheme?: "https" | "http";
|
|
10
|
+
acceptOCIManifests?: boolean;
|
|
11
|
+
acceptManifestLists?: boolean;
|
|
12
|
+
userAgent?: string;
|
|
13
|
+
scopes?: string[];
|
|
14
|
+
adapter: {
|
|
15
|
+
fetch(input: string | Request, init?: RequestInit): Promise<Response>;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=RegistryClientOptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RegistryClientOptions.d.ts","sourceRoot":"","sources":["../../src/client/RegistryClientOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,YAAY,CAAC;IAEpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE;QACP,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;KACvE,CAAC;CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { DockerResponse, RegistryError } from "../types/RegistryTypes.js";
|
|
2
|
+
/** Base class for custom error classes. */
|
|
3
|
+
export declare class ApiError extends Error {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class HttpError extends ApiError {
|
|
7
|
+
resp: DockerResponse;
|
|
8
|
+
errors: RegistryError[];
|
|
9
|
+
name: string;
|
|
10
|
+
constructor(resp: DockerResponse, errors: RegistryError[], message: string);
|
|
11
|
+
}
|
|
12
|
+
export declare class BadDigestError extends ApiError {
|
|
13
|
+
readonly name = "BadDigestError";
|
|
14
|
+
}
|
|
15
|
+
export declare class InvalidContentError extends ApiError {
|
|
16
|
+
readonly name = "InvalidContentError";
|
|
17
|
+
}
|
|
18
|
+
export declare class InternalError extends ApiError {
|
|
19
|
+
readonly name = "InternalError";
|
|
20
|
+
}
|
|
21
|
+
export declare class ManifestVerificationError extends ApiError {
|
|
22
|
+
readonly name = "ManifestVerificationError";
|
|
23
|
+
}
|
|
24
|
+
export declare class InvalidManifestError extends ApiError {
|
|
25
|
+
readonly name = "InvalidManifestError";
|
|
26
|
+
}
|
|
27
|
+
export declare class DownloadError extends ApiError {
|
|
28
|
+
readonly name = "DownloadError";
|
|
29
|
+
}
|
|
30
|
+
export declare class UploadError extends ApiError {
|
|
31
|
+
readonly name = "UploadError";
|
|
32
|
+
}
|
|
33
|
+
export declare class BlobReadError extends ApiError {
|
|
34
|
+
readonly name = "BlobReadError";
|
|
35
|
+
}
|
|
36
|
+
export declare class TooManyRedirectsError extends ApiError {
|
|
37
|
+
readonly name = "TooManyRedirectsError";
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=RegistryErrors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RegistryErrors.d.ts","sourceRoot":"","sources":["../../src/errors/RegistryErrors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAM/E,2CAA2C;AAC3C,qBAAa,QAAS,SAAQ,KAAK;gBACrB,OAAO,EAAE,MAAM;CAK5B;AAED,qBAAa,SAAU,SAAQ,QAAQ;IAG5B,IAAI,EAAE,cAAc;IACpB,MAAM,EAAE,aAAa,EAAE;IAHvB,IAAI,SAAe;gBAEnB,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,aAAa,EAAE,EAC9B,OAAO,EAAE,MAAM;CAIlB;AACD,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,SAAkB,IAAI,oBAAoB;CAC3C;AACD,qBAAa,mBAAoB,SAAQ,QAAQ;IAC/C,SAAkB,IAAI,yBAAyB;CAChD;AAED,qBAAa,aAAc,SAAQ,QAAQ;IACzC,SAAkB,IAAI,mBAAmB;CAC1C;AAED,qBAAa,yBAA0B,SAAQ,QAAQ;IACrD,SAAkB,IAAI,+BAA+B;CACtD;AAED,qBAAa,oBAAqB,SAAQ,QAAQ;IAChD,SAAkB,IAAI,0BAA0B;CACjD;AAED,qBAAa,aAAc,SAAQ,QAAQ;IACzC,SAAkB,IAAI,mBAAmB;CAC1C;AAED,qBAAa,WAAY,SAAQ,QAAQ;IACvC,SAAkB,IAAI,iBAAiB;CACxC;AAED,qBAAa,aAAc,SAAQ,QAAQ;IACzC,SAAkB,IAAI,mBAAmB;CAC1C;AAOD,qBAAa,qBAAsB,SAAQ,QAAQ;IACjD,SAAkB,IAAI,2BAA2B;CAClD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Error classes that docker-registry-client may produce.
|
|
3
|
+
*/
|
|
4
|
+
/** Base class for custom error classes. */
|
|
5
|
+
export class ApiError extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = new.target.name;
|
|
9
|
+
Error.captureStackTrace?.(this, new.target);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class HttpError extends ApiError {
|
|
13
|
+
resp;
|
|
14
|
+
errors;
|
|
15
|
+
name = "HttpError";
|
|
16
|
+
constructor(resp, errors, message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.resp = resp;
|
|
19
|
+
this.errors = errors;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export class BadDigestError extends ApiError {
|
|
23
|
+
name = "BadDigestError";
|
|
24
|
+
}
|
|
25
|
+
export class InvalidContentError extends ApiError {
|
|
26
|
+
name = "InvalidContentError";
|
|
27
|
+
}
|
|
28
|
+
export class InternalError extends ApiError {
|
|
29
|
+
name = "InternalError";
|
|
30
|
+
}
|
|
31
|
+
export class ManifestVerificationError extends ApiError {
|
|
32
|
+
name = "ManifestVerificationError";
|
|
33
|
+
}
|
|
34
|
+
export class InvalidManifestError extends ApiError {
|
|
35
|
+
name = "InvalidManifestError";
|
|
36
|
+
}
|
|
37
|
+
export class DownloadError extends ApiError {
|
|
38
|
+
name = "DownloadError";
|
|
39
|
+
}
|
|
40
|
+
export class UploadError extends ApiError {
|
|
41
|
+
name = "UploadError";
|
|
42
|
+
}
|
|
43
|
+
export class BlobReadError extends ApiError {
|
|
44
|
+
name = "BlobReadError";
|
|
45
|
+
}
|
|
46
|
+
// export class UnauthorizedError extends HttpError {
|
|
47
|
+
// readonly name = 'UnauthorizedError';
|
|
48
|
+
// readonly statusCode = 401;
|
|
49
|
+
// }
|
|
50
|
+
export class TooManyRedirectsError extends ApiError {
|
|
51
|
+
name = "TooManyRedirectsError";
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GhcrConstants.d.ts","sourceRoot":"","sources":["../../src/ghcr/GhcrConstants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,KAAK,0BAA0B,CAAC;AAC7C,eAAO,MAAM,OAAO,YAAY,CAAC;AACjC,eAAO,MAAM,iBAAiB,gBAAgB,CAAC;AAC/C,eAAO,MAAM,iBAAiB,UAAU,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from "./client/OciRegistryClient.js";
|
|
2
|
+
export * from "./types/ManifestTypes.js";
|
|
3
|
+
export * from "./types/RegistryTypes.js";
|
|
4
|
+
export * from "./types/AuthTypes.js";
|
|
5
|
+
export * from "./types/RequestTypes.js";
|
|
6
|
+
export * from "./parsing/RepoParser.js";
|
|
7
|
+
export * from "./parsing/IndexParser.js";
|
|
8
|
+
export * from "./parsing/LinkHeaderParser.js";
|
|
9
|
+
export * from "./utils/ValidationUtils.js";
|
|
10
|
+
export * from "./utils/DigestUtils.js";
|
|
11
|
+
export * from "./errors/RegistryErrors.js";
|
|
12
|
+
export * from "./ghcr/GhcrConstants.js";
|
|
13
|
+
export type { FetchAdapter } from "./client/FetchAdapter.js";
|
|
14
|
+
export type { RegistryClientOptions } from "./client/RegistryClientOptions.js";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Re-export everything from new structure
|
|
2
|
+
export * from "./client/OciRegistryClient.js";
|
|
3
|
+
export * from "./types/ManifestTypes.js";
|
|
4
|
+
export * from "./types/RegistryTypes.js";
|
|
5
|
+
export * from "./types/AuthTypes.js";
|
|
6
|
+
export * from "./types/RequestTypes.js";
|
|
7
|
+
export * from "./parsing/RepoParser.js";
|
|
8
|
+
export * from "./parsing/IndexParser.js";
|
|
9
|
+
export * from "./parsing/LinkHeaderParser.js";
|
|
10
|
+
export * from "./utils/ValidationUtils.js";
|
|
11
|
+
export * from "./utils/DigestUtils.js";
|
|
12
|
+
export * from "./errors/RegistryErrors.js";
|
|
13
|
+
export * from "./ghcr/GhcrConstants.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["// Re-export everything from new structure\nexport * from \"./client/OciRegistryClient.js\";\nexport * from \"./types/ManifestTypes.js\";\nexport * from \"./types/RegistryTypes.js\";\nexport * from \"./types/AuthTypes.js\";\nexport * from \"./types/RequestTypes.js\";\nexport * from \"./parsing/RepoParser.js\";\nexport * from \"./parsing/IndexParser.js\";\nexport * from \"./parsing/LinkHeaderParser.js\";\nexport * from \"./utils/ValidationUtils.js\";\nexport * from \"./utils/DigestUtils.js\";\nexport * from \"./errors/RegistryErrors.js\";\nexport * from \"./ghcr/GhcrConstants.js\";\nexport type { FetchAdapter } from \"./client/FetchAdapter.js\";\nexport type { RegistryClientOptions } from \"./client/RegistryClientOptions.js\";\n"],
|
|
5
|
+
"mappings": "AACA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { RegistryIndex } from "../types/RegistryTypes.js";
|
|
2
|
+
export declare const DEFAULT_INDEX_NAME = "docker.io";
|
|
3
|
+
export declare const DEFAULT_INDEX_URL = "https://registry-1.docker.io";
|
|
4
|
+
export declare const DEFAULT_LOGIN_SERVERNAME = "https://index.docker.io/v1/";
|
|
5
|
+
/**
|
|
6
|
+
* Parse a docker index name or index URL.
|
|
7
|
+
*
|
|
8
|
+
* Examples:
|
|
9
|
+
* docker.io (no scheme implies 'https')
|
|
10
|
+
* index.docker.io (normalized to docker.io)
|
|
11
|
+
* https://docker.io
|
|
12
|
+
* http://localhost:5000
|
|
13
|
+
* https://index.docker.io/v1/ (special case)
|
|
14
|
+
*
|
|
15
|
+
* Special case: `docker` still refers to "https://index.docker.io/v1/"
|
|
16
|
+
* when dealing with auth (including in its json file).
|
|
17
|
+
*
|
|
18
|
+
* @param {String} arg: Optional. Index name (optionally with leading scheme).
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseIndex(arg?: string): RegistryIndex;
|
|
21
|
+
/**
|
|
22
|
+
* Similar in spirit to docker.git:registry/endpoint.go#NewEndpoint().
|
|
23
|
+
*/
|
|
24
|
+
export declare function urlFromIndex(index: RegistryIndex, scheme?: "http" | "https"): string;
|
|
25
|
+
export declare function isLocalhost(host: string): boolean;
|
|
26
|
+
//# sourceMappingURL=IndexParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IndexParser.d.ts","sourceRoot":"","sources":["../../src/parsing/IndexParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAK/D,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAC9C,eAAO,MAAM,iBAAiB,iCAAiC,CAAC;AAEhE,eAAO,MAAM,wBAAwB,gCAAgC,CAAC;AAEtE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,aAAa,CAwEtD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,aAAa,EACpB,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,GACxB,MAAM,CAeR;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOjD"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// --- globals
|
|
2
|
+
// See `INDEXNAME` in docker/docker.git:registry/config.go.
|
|
3
|
+
export const DEFAULT_INDEX_NAME = "docker.io";
|
|
4
|
+
export const DEFAULT_INDEX_URL = "https://registry-1.docker.io";
|
|
5
|
+
export const DEFAULT_LOGIN_SERVERNAME = "https://index.docker.io/v1/";
|
|
6
|
+
/**
|
|
7
|
+
* Parse a docker index name or index URL.
|
|
8
|
+
*
|
|
9
|
+
* Examples:
|
|
10
|
+
* docker.io (no scheme implies 'https')
|
|
11
|
+
* index.docker.io (normalized to docker.io)
|
|
12
|
+
* https://docker.io
|
|
13
|
+
* http://localhost:5000
|
|
14
|
+
* https://index.docker.io/v1/ (special case)
|
|
15
|
+
*
|
|
16
|
+
* Special case: `docker` still refers to "https://index.docker.io/v1/"
|
|
17
|
+
* when dealing with auth (including in its json file).
|
|
18
|
+
*
|
|
19
|
+
* @param {String} arg: Optional. Index name (optionally with leading scheme).
|
|
20
|
+
*/
|
|
21
|
+
export function parseIndex(arg) {
|
|
22
|
+
if (!arg || arg === DEFAULT_LOGIN_SERVERNAME) {
|
|
23
|
+
// Default index.
|
|
24
|
+
return {
|
|
25
|
+
scheme: "https",
|
|
26
|
+
name: DEFAULT_INDEX_NAME,
|
|
27
|
+
official: true,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// Optional protocol/scheme.
|
|
31
|
+
let indexName;
|
|
32
|
+
let scheme = "https";
|
|
33
|
+
const protoSepIdx = arg.indexOf("://");
|
|
34
|
+
if (protoSepIdx !== -1) {
|
|
35
|
+
const foundScheme = arg.slice(0, protoSepIdx);
|
|
36
|
+
if (foundScheme !== "http" && foundScheme !== "https") {
|
|
37
|
+
throw new Error("invalid index scheme, must be " + '"http" or "https": ' + arg);
|
|
38
|
+
}
|
|
39
|
+
scheme = foundScheme;
|
|
40
|
+
indexName = arg.slice(protoSepIdx + 3);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
scheme = isLocalhost(arg) ? "http" : "https";
|
|
44
|
+
indexName = arg;
|
|
45
|
+
}
|
|
46
|
+
if (!indexName) {
|
|
47
|
+
throw new Error("invalid index, empty host: " + arg);
|
|
48
|
+
}
|
|
49
|
+
else if (indexName.indexOf(".") === -1 &&
|
|
50
|
+
indexName.indexOf(":") === -1 &&
|
|
51
|
+
indexName !== "localhost") {
|
|
52
|
+
throw new Error(`invalid index, "${indexName}" does not look like a valid host: ${arg}`);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// Allow a trailing '/' as from some URL builder functions that
|
|
56
|
+
// add a default '/' path to a URL, e.g. 'https://docker.io/'.
|
|
57
|
+
if (indexName[indexName.length - 1] === "/") {
|
|
58
|
+
indexName = indexName.slice(0, indexName.length - 1);
|
|
59
|
+
}
|
|
60
|
+
// Ensure no trailing repo.
|
|
61
|
+
if (indexName.indexOf("/") !== -1) {
|
|
62
|
+
throw new Error("invalid index, trailing repo: " + arg);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Per docker.git's `ValidateIndexName`.
|
|
66
|
+
if (indexName === "index." + DEFAULT_INDEX_NAME) {
|
|
67
|
+
indexName = DEFAULT_INDEX_NAME;
|
|
68
|
+
}
|
|
69
|
+
const index = {
|
|
70
|
+
name: indexName,
|
|
71
|
+
official: indexName === DEFAULT_INDEX_NAME,
|
|
72
|
+
scheme,
|
|
73
|
+
};
|
|
74
|
+
// Disallow official and 'http'.
|
|
75
|
+
if (index.official && index.scheme === "http") {
|
|
76
|
+
throw new Error("invalid index, plaintext HTTP to official index " +
|
|
77
|
+
"is disallowed: " +
|
|
78
|
+
arg);
|
|
79
|
+
}
|
|
80
|
+
return index;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Similar in spirit to docker.git:registry/endpoint.go#NewEndpoint().
|
|
84
|
+
*/
|
|
85
|
+
export function urlFromIndex(index, scheme) {
|
|
86
|
+
if (index.official) {
|
|
87
|
+
// v1
|
|
88
|
+
if (scheme != null && scheme !== "https")
|
|
89
|
+
throw new Error(`Unencrypted communication with docker.io is not allowed`);
|
|
90
|
+
return DEFAULT_INDEX_URL;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
if (scheme != null && scheme !== "https" && scheme !== "http")
|
|
94
|
+
throw new Error(`Non-HTTP communication with docker registries is not allowed`);
|
|
95
|
+
return `${scheme ?? index.scheme}://${index.name}`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export function isLocalhost(host) {
|
|
99
|
+
const lead = host.split(":")[0];
|
|
100
|
+
if (lead === "localhost" || lead === "127.0.0.1" || host.includes("::1")) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LinkHeaderParser.d.ts","sourceRoot":"","sources":["../../src/parsing/LinkHeaderParser.ts"],"names":[],"mappings":"AAAA,KAAK,IAAI,GAAG;IACV,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC,CAAC;AAGF,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,EAAE,CA+BhE"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const linkRegex = /^<([^>]+)>(?:\s*;\s*(.+))?$/;
|
|
2
|
+
export function parseLinkHeader(rawHeader) {
|
|
3
|
+
if (!rawHeader)
|
|
4
|
+
return [];
|
|
5
|
+
return rawHeader
|
|
6
|
+
.split(",")
|
|
7
|
+
.slice(0, 5) // Arbitrary limit to how many links we are willing to parse
|
|
8
|
+
.flatMap((piece) => {
|
|
9
|
+
const matches = piece.trim().match(linkRegex);
|
|
10
|
+
if (!matches)
|
|
11
|
+
return [];
|
|
12
|
+
const { rel, ...params } = matches[2]
|
|
13
|
+
?.split(";")
|
|
14
|
+
.map((param) => param.trim().split("="))
|
|
15
|
+
.reduce((acc, [key, value]) => {
|
|
16
|
+
if (!value)
|
|
17
|
+
return acc;
|
|
18
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
19
|
+
value = value.slice(1, -1);
|
|
20
|
+
}
|
|
21
|
+
acc[key] = value;
|
|
22
|
+
return acc;
|
|
23
|
+
}, {}) ?? {};
|
|
24
|
+
if (!rel)
|
|
25
|
+
return [];
|
|
26
|
+
return [
|
|
27
|
+
{
|
|
28
|
+
rel,
|
|
29
|
+
url: matches[1],
|
|
30
|
+
params,
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { RegistryImage, RegistryIndex, RegistryRepo } from "../types/RegistryTypes.js";
|
|
2
|
+
export declare const DEFAULT_TAG = "latest";
|
|
3
|
+
/**
|
|
4
|
+
* Parse a docker repo and tag string: [INDEX/]REPO[:TAG|@DIGEST]
|
|
5
|
+
*
|
|
6
|
+
* Examples:
|
|
7
|
+
* busybox
|
|
8
|
+
* google/python
|
|
9
|
+
* docker.io/ubuntu
|
|
10
|
+
* localhost:5000/blarg
|
|
11
|
+
* http://localhost:5000/blarg
|
|
12
|
+
*
|
|
13
|
+
* Dev Notes:
|
|
14
|
+
* - This is meant to mimic
|
|
15
|
+
* docker.git:registry/config.go#ServiceConfig.NewRepositoryInfo
|
|
16
|
+
* as much as reasonable -- with the addition that we maintain the
|
|
17
|
+
* 'tag' field. Also, that we accept the scheme on the "INDEX" is
|
|
18
|
+
* different than docker.git's parsing.
|
|
19
|
+
* - TODO: what about the '@digest' digest alternative to a tag? See:
|
|
20
|
+
* // JSSTYLED
|
|
21
|
+
* https://github.com/docker/docker/blob/0c7b51089c8cd7ef3510a9b40edaa139a7ca91aa/pkg/parsers/parsers.go#L68
|
|
22
|
+
*
|
|
23
|
+
* @param arg {String} The docker repo string to parse. See examples above.
|
|
24
|
+
* @param defaultIndex {Object|String} Optional. The default index to use
|
|
25
|
+
* if not specified with `arg`. If not given the default is 'docker.io'.
|
|
26
|
+
* If given it may either be a string, e.g. 'https://myreg.example.com',
|
|
27
|
+
* or parsed index object, as from `parseIndex()`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseRepo(arg: string, defaultIndex?: string | RegistryIndex): RegistryRepo;
|
|
30
|
+
/**
|
|
31
|
+
* Parse a docker repo and tag/digest string: [INDEX/]REPO[:TAG|@DIGEST|:TAG@DIGEST]
|
|
32
|
+
*
|
|
33
|
+
* Examples:
|
|
34
|
+
* busybox
|
|
35
|
+
* busybox:latest
|
|
36
|
+
* google/python:3.3
|
|
37
|
+
* docker.io/ubuntu
|
|
38
|
+
* localhost:5000/blarg
|
|
39
|
+
* http://localhost:5000/blarg:latest
|
|
40
|
+
* google/python:3.3@sha256:fb9f16730ac6316afa4d97caa51302199...
|
|
41
|
+
* alpine@sha256:fb9f16730ac6316afa4d97caa5130219927bfcecf0b0...
|
|
42
|
+
*
|
|
43
|
+
* Dev Notes:
|
|
44
|
+
* - TODO Validation on digest and tag would be nice.
|
|
45
|
+
*
|
|
46
|
+
* @param arg {String} The docker repo:tag string to parse. See examples above.
|
|
47
|
+
* @param defaultIndex {Object|String} Optional. The default index to use
|
|
48
|
+
* if not specified with `arg`. If not given the default is 'docker.io'.
|
|
49
|
+
* If given it may either be a string, e.g. 'https://myreg.example.com',
|
|
50
|
+
* or parsed index object, as from `parseIndex()`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function parseRepoAndRef(arg: string, defaultIndex?: string | RegistryIndex): RegistryImage;
|
|
53
|
+
export declare const parseRepoAndTag: typeof parseRepoAndRef;
|
|
54
|
+
//# sourceMappingURL=RepoParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RepoParser.d.ts","sourceRoot":"","sources":["../../src/parsing/RepoParser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,YAAY,EACb,MAAM,2BAA2B,CAAC;AAMnC,eAAO,MAAM,WAAW,WAAW,CAAC;AAKpC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,MAAM,EACX,YAAY,CAAC,EAAE,MAAM,GAAG,aAAa,GACpC,YAAY,CAkHd;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,YAAY,CAAC,EAAE,MAAM,GAAG,aAAa,GACpC,aAAa,CAgCf;AAED,eAAO,MAAM,eAAe,wBAAkB,CAAC"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { parseIndex } from "./IndexParser.js";
|
|
2
|
+
import { splitIntoTwo } from "../utils/ValidationUtils.js";
|
|
3
|
+
// JSSTYLED
|
|
4
|
+
// 'DEFAULTTAG' from https://github.com/docker/docker/blob/0c7b51089c8cd7ef3510a9b40edaa139a7ca91aa/graph/tags.go#L25
|
|
5
|
+
export const DEFAULT_TAG = "latest";
|
|
6
|
+
const VALID_NS = /^[a-z0-9._-]*$/;
|
|
7
|
+
const VALID_REPO = /^[a-z0-9_/.-]*$/;
|
|
8
|
+
/**
|
|
9
|
+
* Parse a docker repo and tag string: [INDEX/]REPO[:TAG|@DIGEST]
|
|
10
|
+
*
|
|
11
|
+
* Examples:
|
|
12
|
+
* busybox
|
|
13
|
+
* google/python
|
|
14
|
+
* docker.io/ubuntu
|
|
15
|
+
* localhost:5000/blarg
|
|
16
|
+
* http://localhost:5000/blarg
|
|
17
|
+
*
|
|
18
|
+
* Dev Notes:
|
|
19
|
+
* - This is meant to mimic
|
|
20
|
+
* docker.git:registry/config.go#ServiceConfig.NewRepositoryInfo
|
|
21
|
+
* as much as reasonable -- with the addition that we maintain the
|
|
22
|
+
* 'tag' field. Also, that we accept the scheme on the "INDEX" is
|
|
23
|
+
* different than docker.git's parsing.
|
|
24
|
+
* - TODO: what about the '@digest' digest alternative to a tag? See:
|
|
25
|
+
* // JSSTYLED
|
|
26
|
+
* https://github.com/docker/docker/blob/0c7b51089c8cd7ef3510a9b40edaa139a7ca91aa/pkg/parsers/parsers.go#L68
|
|
27
|
+
*
|
|
28
|
+
* @param arg {String} The docker repo string to parse. See examples above.
|
|
29
|
+
* @param defaultIndex {Object|String} Optional. The default index to use
|
|
30
|
+
* if not specified with `arg`. If not given the default is 'docker.io'.
|
|
31
|
+
* If given it may either be a string, e.g. 'https://myreg.example.com',
|
|
32
|
+
* or parsed index object, as from `parseIndex()`.
|
|
33
|
+
*/
|
|
34
|
+
export function parseRepo(arg, defaultIndex) {
|
|
35
|
+
let index;
|
|
36
|
+
// Strip off optional leading `INDEX/`, parse it to `info.index` and
|
|
37
|
+
// leave the rest in `remoteName`.
|
|
38
|
+
let remoteNameRaw;
|
|
39
|
+
const protoSepIdx = arg.indexOf("://");
|
|
40
|
+
if (protoSepIdx !== -1) {
|
|
41
|
+
// (A) repo with a protocol, e.g. 'https://host/repo'.
|
|
42
|
+
const slashIdx = arg.indexOf("/", protoSepIdx + 3);
|
|
43
|
+
if (slashIdx === -1) {
|
|
44
|
+
throw new Error('invalid repository name, no "/REPO" after ' + "hostame: " + arg);
|
|
45
|
+
}
|
|
46
|
+
const indexName = arg.slice(0, slashIdx);
|
|
47
|
+
remoteNameRaw = arg.slice(slashIdx + 1);
|
|
48
|
+
index = parseIndex(indexName);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
const parts = splitIntoTwo(arg, "/");
|
|
52
|
+
if (parts.length === 1 ||
|
|
53
|
+
/* or if parts[0] doesn't look like a hostname or IP */
|
|
54
|
+
(parts[0].indexOf(".") === -1 &&
|
|
55
|
+
parts[0].indexOf(":") === -1 &&
|
|
56
|
+
parts[0] !== "localhost")) {
|
|
57
|
+
// (B) repo without leading 'INDEX/'.
|
|
58
|
+
if (defaultIndex === undefined) {
|
|
59
|
+
index = parseIndex();
|
|
60
|
+
}
|
|
61
|
+
else if (typeof defaultIndex === "string") {
|
|
62
|
+
index = parseIndex(defaultIndex);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
index = defaultIndex;
|
|
66
|
+
}
|
|
67
|
+
remoteNameRaw = arg;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// (C) repo with leading 'INDEX/' (without protocol).
|
|
71
|
+
index = parseIndex(parts[0]);
|
|
72
|
+
remoteNameRaw = parts[1];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Validate remoteName (docker `validateRemoteName`).
|
|
76
|
+
const nameParts = splitIntoTwo(remoteNameRaw, "/");
|
|
77
|
+
let ns = "", name;
|
|
78
|
+
if (nameParts.length === 2) {
|
|
79
|
+
name = nameParts[1];
|
|
80
|
+
// Validate ns.
|
|
81
|
+
ns = nameParts[0];
|
|
82
|
+
if (ns.length < 2 || ns.length > 255) {
|
|
83
|
+
throw new Error("invalid repository namespace, must be between " +
|
|
84
|
+
"2 and 255 characters: " +
|
|
85
|
+
ns);
|
|
86
|
+
}
|
|
87
|
+
if (!VALID_NS.test(ns)) {
|
|
88
|
+
throw new Error("invalid repository namespace, may only contain " +
|
|
89
|
+
"[a-z0-9._-] characters: " +
|
|
90
|
+
ns);
|
|
91
|
+
}
|
|
92
|
+
if (ns[0] === "-" && ns[ns.length - 1] === "-") {
|
|
93
|
+
throw new Error("invalid repository namespace, cannot start or " +
|
|
94
|
+
"end with a hypen: " +
|
|
95
|
+
ns);
|
|
96
|
+
}
|
|
97
|
+
if (ns.indexOf("--") !== -1) {
|
|
98
|
+
throw new Error("invalid repository namespace, cannot contain " +
|
|
99
|
+
"consecutive hyphens: " +
|
|
100
|
+
ns);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
name = remoteNameRaw;
|
|
105
|
+
if (index.official) {
|
|
106
|
+
ns = "library";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Validate name.
|
|
110
|
+
if (!VALID_REPO.test(name)) {
|
|
111
|
+
throw new Error("invalid repository name, may only contain " +
|
|
112
|
+
"[a-z0-9_/.-] characters: " +
|
|
113
|
+
name);
|
|
114
|
+
}
|
|
115
|
+
const isLibrary = index.official && ns === "library";
|
|
116
|
+
const remoteName = ns ? `${ns}/${name}` : name;
|
|
117
|
+
const localName = index.official
|
|
118
|
+
? isLibrary
|
|
119
|
+
? name
|
|
120
|
+
: remoteName
|
|
121
|
+
: `${index.name}/${remoteName}`;
|
|
122
|
+
const canonicalName = index.official
|
|
123
|
+
? `${parseIndex().name}/${localName}`
|
|
124
|
+
: localName;
|
|
125
|
+
return {
|
|
126
|
+
index,
|
|
127
|
+
official: isLibrary,
|
|
128
|
+
remoteName,
|
|
129
|
+
localName,
|
|
130
|
+
canonicalName,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Parse a docker repo and tag/digest string: [INDEX/]REPO[:TAG|@DIGEST|:TAG@DIGEST]
|
|
135
|
+
*
|
|
136
|
+
* Examples:
|
|
137
|
+
* busybox
|
|
138
|
+
* busybox:latest
|
|
139
|
+
* google/python:3.3
|
|
140
|
+
* docker.io/ubuntu
|
|
141
|
+
* localhost:5000/blarg
|
|
142
|
+
* http://localhost:5000/blarg:latest
|
|
143
|
+
* google/python:3.3@sha256:fb9f16730ac6316afa4d97caa51302199...
|
|
144
|
+
* alpine@sha256:fb9f16730ac6316afa4d97caa5130219927bfcecf0b0...
|
|
145
|
+
*
|
|
146
|
+
* Dev Notes:
|
|
147
|
+
* - TODO Validation on digest and tag would be nice.
|
|
148
|
+
*
|
|
149
|
+
* @param arg {String} The docker repo:tag string to parse. See examples above.
|
|
150
|
+
* @param defaultIndex {Object|String} Optional. The default index to use
|
|
151
|
+
* if not specified with `arg`. If not given the default is 'docker.io'.
|
|
152
|
+
* If given it may either be a string, e.g. 'https://myreg.example.com',
|
|
153
|
+
* or parsed index object, as from `parseIndex()`.
|
|
154
|
+
*/
|
|
155
|
+
export function parseRepoAndRef(arg, defaultIndex) {
|
|
156
|
+
// Parse off the tag/digest per
|
|
157
|
+
// https://github.com/docker/docker/blob/0c7b51089c8cd7ef3510a9b40edaa139a7ca91aa/pkg/parsers/parsers.go#L69
|
|
158
|
+
let digest = null;
|
|
159
|
+
let tag = null;
|
|
160
|
+
const atIdx = arg.lastIndexOf("@");
|
|
161
|
+
if (atIdx !== -1) {
|
|
162
|
+
digest = arg.slice(atIdx + 1);
|
|
163
|
+
arg = arg.slice(0, atIdx);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
tag = DEFAULT_TAG;
|
|
167
|
+
}
|
|
168
|
+
const colonIdx = arg.lastIndexOf(":");
|
|
169
|
+
const slashIdx = arg.lastIndexOf("/");
|
|
170
|
+
if (colonIdx !== -1 && colonIdx > slashIdx) {
|
|
171
|
+
tag = arg.slice(colonIdx + 1);
|
|
172
|
+
arg = arg.slice(0, colonIdx);
|
|
173
|
+
}
|
|
174
|
+
const repo = parseRepo(arg, defaultIndex);
|
|
175
|
+
return {
|
|
176
|
+
...repo,
|
|
177
|
+
digest,
|
|
178
|
+
tag,
|
|
179
|
+
canonicalRef: [
|
|
180
|
+
repo.canonicalName,
|
|
181
|
+
tag ? `:${tag}` : "",
|
|
182
|
+
digest ? `@${digest}` : "",
|
|
183
|
+
].join(""),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
export const parseRepoAndTag = parseRepoAndRef;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication types for registry operations
|
|
3
|
+
*/
|
|
4
|
+
export type AuthInfo = {
|
|
5
|
+
type: "None";
|
|
6
|
+
} | {
|
|
7
|
+
type: "Basic";
|
|
8
|
+
username: string;
|
|
9
|
+
password: string;
|
|
10
|
+
} | {
|
|
11
|
+
type: "Bearer";
|
|
12
|
+
token: string;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=AuthTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthTypes.d.ts","sourceRoot":"","sources":["../../src/types/AuthTypes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC"}
|