@serve.zone/appstore 32.27.0 → 33.0.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/.smartconfig.json +4 -1
- package/changelog.md +57 -0
- package/dist_catalog/catalog.digest +1 -0
- package/dist_catalog/catalog.envelope.json +7440 -0
- package/{dist_ts_client/data.generated.js → dist_catalog/catalog.json} +6740 -6913
- package/dist_ts_client/catalog.d.ts +16 -0
- package/dist_ts_client/catalog.js +12 -0
- package/dist_ts_client/index.d.ts +3 -1
- package/dist_ts_client/index.js +4 -2
- package/dist_ts_client/loader.d.ts +69 -0
- package/dist_ts_client/loader.js +361 -0
- package/dist_ts_client/plugins.d.ts +5 -0
- package/dist_ts_client/plugins.js +8 -0
- package/dist_ts_client/projection.d.ts +59 -0
- package/dist_ts_client/projection.js +83 -0
- package/dist_ts_client/storage.d.ts +15 -9
- package/dist_ts_client/storage.js +81 -657
- package/dist_ts_client/types.d.ts +86 -41
- package/dist_ts_client/types.js +1 -1
- package/dist_ts_testing/fixtures.d.ts +83 -0
- package/dist_ts_testing/fixtures.js +146 -0
- package/dist_ts_testing/index.d.ts +1 -0
- package/dist_ts_testing/index.js +2 -0
- package/dist_ts_testing/plugins.d.ts +7 -0
- package/dist_ts_testing/plugins.js +11 -0
- package/package.json +15 -10
- package/readme.md +161 -92
- package/ts_client/catalog.ts +26 -0
- package/ts_client/index.ts +3 -1
- package/ts_client/loader.ts +414 -0
- package/ts_client/plugins.ts +10 -0
- package/ts_client/projection.ts +148 -0
- package/ts_client/readme.md +17 -27
- package/ts_client/storage.ts +93 -882
- package/ts_client/types.ts +102 -42
- package/ts_testing/fixtures.ts +230 -0
- package/ts_testing/index.ts +1 -0
- package/ts_testing/plugins.ts +15 -0
- package/ts_testing/tspublish.json +3 -0
- package/apps/adminer/app.json +0 -10
- package/apps/adminer/versions/1.0.0/config.json +0 -6
- package/apps/cloudly/app.json +0 -14
- package/apps/cloudly/versions/1.0.0/config.json +0 -120
- package/apps/cloudly/versions/1.1.0/config.json +0 -120
- package/apps/cloudly/versions/1.1.1/config.json +0 -120
- package/apps/cloudly/versions/1.1.10/config.json +0 -173
- package/apps/cloudly/versions/1.1.2/config.json +0 -130
- package/apps/cloudly/versions/1.1.3/config.json +0 -130
- package/apps/cloudly/versions/1.1.4/config.json +0 -130
- package/apps/cloudly/versions/1.1.5/config.json +0 -149
- package/apps/cloudly/versions/1.1.6/config.json +0 -149
- package/apps/cloudly/versions/1.1.7/config.json +0 -149
- package/apps/cloudly/versions/1.1.8/config.json +0 -173
- package/apps/cloudly/versions/1.1.9/config.json +0 -173
- package/apps/cloudly/versions/1.10.0/config.json +0 -173
- package/apps/cloudly/versions/1.11.0/config.json +0 -173
- package/apps/cloudly/versions/1.12.0/config.json +0 -173
- package/apps/cloudly/versions/1.13.0/config.json +0 -173
- package/apps/cloudly/versions/1.14.0/config.json +0 -173
- package/apps/cloudly/versions/1.15.0/config.json +0 -173
- package/apps/cloudly/versions/1.16.0/config.json +0 -173
- package/apps/cloudly/versions/1.17.0/config.json +0 -173
- package/apps/cloudly/versions/1.18.0/config.json +0 -173
- package/apps/cloudly/versions/1.19.0/config.json +0 -173
- package/apps/cloudly/versions/1.2.0/config.json +0 -173
- package/apps/cloudly/versions/1.2.1/config.json +0 -173
- package/apps/cloudly/versions/1.20.0/config.json +0 -173
- package/apps/cloudly/versions/1.21.0/config.json +0 -173
- package/apps/cloudly/versions/1.22.0/config.json +0 -173
- package/apps/cloudly/versions/1.23.0/config.json +0 -173
- package/apps/cloudly/versions/1.24.0/config.json +0 -173
- package/apps/cloudly/versions/1.25.0/config.json +0 -173
- package/apps/cloudly/versions/1.26.0/config.json +0 -173
- package/apps/cloudly/versions/1.27.0/config.json +0 -173
- package/apps/cloudly/versions/1.3.0/config.json +0 -173
- package/apps/cloudly/versions/1.4.0/config.json +0 -173
- package/apps/cloudly/versions/1.5.0/config.json +0 -173
- package/apps/cloudly/versions/1.6.0/config.json +0 -173
- package/apps/cloudly/versions/1.7.0/config.json +0 -173
- package/apps/cloudly/versions/1.8.0/config.json +0 -173
- package/apps/cloudly/versions/1.9.0/config.json +0 -173
- package/apps/ghost/app.json +0 -10
- package/apps/ghost/versions/1.0.0/config.json +0 -18
- package/apps/gitea/app.json +0 -10
- package/apps/gitea/versions/1.0.0/config.json +0 -16
- package/apps/gitops/app.json +0 -14
- package/apps/gitops/versions/1.0.0/config.json +0 -28
- package/apps/grafana/app.json +0 -10
- package/apps/grafana/versions/1.0.0/config.json +0 -9
- package/apps/mariadb/app.json +0 -10
- package/apps/mariadb/versions/1.0.0/config.json +0 -9
- package/apps/mattermost/app.json +0 -10
- package/apps/mattermost/versions/1.0.0/config.json +0 -6
- package/apps/n8n/app.json +0 -10
- package/apps/n8n/versions/1.0.0/config.json +0 -6
- package/apps/nextcloud/app.json +0 -10
- package/apps/nextcloud/versions/1.0.0/config.json +0 -16
- package/apps/nginx/app.json +0 -10
- package/apps/nginx/versions/1.0.0/config.json +0 -6
- package/apps/openbao/app.json +0 -16
- package/apps/openbao/versions/1.0.0/config.json +0 -52
- package/apps/plausible/app.json +0 -10
- package/apps/plausible/versions/1.0.0/config.json +0 -9
- package/apps/portainer/app.json +0 -10
- package/apps/portainer/versions/1.0.0/config.json +0 -6
- package/apps/postgres/app.json +0 -10
- package/apps/postgres/versions/1.0.0/config.json +0 -11
- package/apps/rustdesk-server/app.json +0 -14
- package/apps/rustdesk-server/versions/1.0.0/config.json +0 -19
- package/apps/siprouter/app.json +0 -14
- package/apps/siprouter/versions/1.0.0/config.json +0 -84
- package/apps/uptime-kuma/app.json +0 -10
- package/apps/uptime-kuma/versions/1.0.0/config.json +0 -6
- package/apps/valkey/app.json +0 -10
- package/apps/valkey/versions/1.0.0/config.json +0 -6
- package/apps/vaultwarden/app.json +0 -10
- package/apps/vaultwarden/versions/1.0.0/config.json +0 -6
- package/apps/wordpress/app.json +0 -10
- package/apps/wordpress/versions/1.0.0/config.json +0 -15
- package/appstore.json +0 -194
- package/dist_ts_client/classes.appstoreresolver.d.ts +0 -59
- package/dist_ts_client/classes.appstoreresolver.js +0 -736
- package/dist_ts_client/data.generated.d.ts +0 -2
- package/ts_client/classes.appstoreresolver.ts +0 -896
- package/ts_client/data.generated.ts +0 -7606
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type * as plugins from './plugins.js';
|
|
2
|
+
type TCatalog = plugins.servezoneInterfaces.appstore.IAppStoreCatalog;
|
|
3
|
+
type TCatalogApp = plugins.servezoneInterfaces.appstore.IAppStoreCatalogApp;
|
|
4
|
+
type TCatalogVersion = plugins.servezoneInterfaces.appstore.IAppStoreCatalogVersion;
|
|
5
|
+
/** The npm package that carries the signed App Store catalog. */
|
|
6
|
+
export declare const appStoreCatalogPackageName = "@serve.zone/appstore";
|
|
7
|
+
/** Where the signed envelope sits inside the published package (and its `./catalog.envelope.json` export). */
|
|
8
|
+
export declare const appStoreCatalogEnvelopePackagePath = "dist_catalog/catalog.envelope.json";
|
|
9
|
+
/** The app a catalog lists under `appIdArg`. */
|
|
10
|
+
export declare const findAppStoreCatalogApp: (catalogArg: TCatalog, appIdArg: string) => TCatalogApp | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* The listed `app@version`, withdrawn ones included: an installed service keeps reading its
|
|
13
|
+
* version. Whether it is an install or upgrade target is `appstore.judgeAppStoreCatalogInstallTarget`.
|
|
14
|
+
*/
|
|
15
|
+
export declare const findAppStoreCatalogVersion: (catalogArg: TCatalog, appIdArg: string, versionArg: string) => TCatalogVersion | undefined;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** The npm package that carries the signed App Store catalog. */
|
|
2
|
+
export const appStoreCatalogPackageName = '@serve.zone/appstore';
|
|
3
|
+
/** Where the signed envelope sits inside the published package (and its `./catalog.envelope.json` export). */
|
|
4
|
+
export const appStoreCatalogEnvelopePackagePath = 'dist_catalog/catalog.envelope.json';
|
|
5
|
+
/** The app a catalog lists under `appIdArg`. */
|
|
6
|
+
export const findAppStoreCatalogApp = (catalogArg, appIdArg) => catalogArg.apps.find((appArg) => appArg.info.id === appIdArg);
|
|
7
|
+
/**
|
|
8
|
+
* The listed `app@version`, withdrawn ones included: an installed service keeps reading its
|
|
9
|
+
* version. Whether it is an install or upgrade target is `appstore.judgeAppStoreCatalogInstallTarget`.
|
|
10
|
+
*/
|
|
11
|
+
export const findAppStoreCatalogVersion = (catalogArg, appIdArg, versionArg) => findAppStoreCatalogApp(catalogArg, appIdArg)?.versions.find((entryArg) => entryArg.version === versionArg);
|
|
12
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2F0YWxvZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX2NsaWVudC9jYXRhbG9nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQU1BLGlFQUFpRTtBQUNqRSxNQUFNLENBQUMsTUFBTSwwQkFBMEIsR0FBRyxzQkFBc0IsQ0FBQztBQUVqRSw4R0FBOEc7QUFDOUcsTUFBTSxDQUFDLE1BQU0sa0NBQWtDLEdBQUcsb0NBQW9DLENBQUM7QUFFdkYsZ0RBQWdEO0FBQ2hELE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUFHLENBQUMsVUFBb0IsRUFBRSxRQUFnQixFQUEyQixFQUFFLENBQ3hHLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxRQUFRLENBQUMsQ0FBQztBQUVoRTs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSwwQkFBMEIsR0FBRyxDQUN4QyxVQUFvQixFQUNwQixRQUFnQixFQUNoQixVQUFrQixFQUNXLEVBQUUsQ0FDL0Isc0JBQXNCLENBQUMsVUFBVSxFQUFFLFFBQVEsQ0FBQyxFQUFFLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEtBQUssVUFBVSxDQUFDLENBQUMifQ==
|
package/dist_ts_client/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './catalog.js';
|
|
2
|
+
export * from './loader.js';
|
|
3
|
+
export * from './projection.js';
|
|
2
4
|
export * from './storage.js';
|
|
3
5
|
export * from './types.js';
|
|
4
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19jbGllbnQvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxjQUFjLENBQUM7QUFDN0IsY0FBYyxhQUFhLENBQUM7QUFDNUIsY0FBYyxpQkFBaUIsQ0FBQztBQUNoQyxjQUFjLGNBQWMsQ0FBQztBQUM3QixjQUFjLFlBQVksQ0FBQyJ9
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import type { IAppStoreCatalogLoadOptions, IAppStoreCatalogLoaderOptions, IAppStoreCatalogStoredRecord, TAppStoreCatalogJudgement, TAppStoreCatalogLoadResult, TAppStoreCatalogRestoreResult } from './types.js';
|
|
3
|
+
type TEnvelope = plugins.servezoneInterfaces.appstore.IAppStoreCatalogEnvelope;
|
|
4
|
+
/**
|
|
5
|
+
* Loads the signed App Store catalog from npm registries and judges it against the consumer's
|
|
6
|
+
* active catalog. It holds the consumer's options only: no timer, no schedule, no catalog.
|
|
7
|
+
*
|
|
8
|
+
* One load asks every configured registry for the package its `latest` dist-tag names, downloads
|
|
9
|
+
* the tarball, checks it against the registry's `dist.integrity`, extracts the envelope, verifies
|
|
10
|
+
* it against the trusted keys (`verifyAppStoreCatalogEnvelope`) and judges its succession
|
|
11
|
+
* (`judgeAppStoreCatalogSuccession`). The first registry whose catalog activates ends the load;
|
|
12
|
+
* otherwise the result is the first `unchanged`, else the first `refused`, else `failed`, and
|
|
13
|
+
* `registries` reports what each registry answered.
|
|
14
|
+
*
|
|
15
|
+
* The loader holds no state, runs no schedule and persists nothing: the consumer's catalog runtime
|
|
16
|
+
* owns the one refresh schedule of its process, passes its active envelope in, stores an activated
|
|
17
|
+
* one as its last-known-good catalog and restores it with `restoreEnvelopeText` at start. Every
|
|
18
|
+
* request is bounded in time and size; a transport, integrity or packaging failure and every
|
|
19
|
+
* refusal are results, never thrown. Aborting `signal` (the consumer stopping) ends the load at
|
|
20
|
+
* once and rejects it with the signal's reason; no attempt is reported for it.
|
|
21
|
+
*/
|
|
22
|
+
export declare class AppStoreCatalogLoader {
|
|
23
|
+
private readonly trustedKeys;
|
|
24
|
+
private readonly supportedFeatureIds;
|
|
25
|
+
private readonly registries;
|
|
26
|
+
private readonly fetchFunction;
|
|
27
|
+
private readonly now;
|
|
28
|
+
private readonly requestTimeoutMs;
|
|
29
|
+
private readonly maximumTarballBytes;
|
|
30
|
+
private readonly maximumUnpackedBytes;
|
|
31
|
+
constructor(optionsArg: IAppStoreCatalogLoaderOptions);
|
|
32
|
+
/** One load from the configured registries; see the class description. */
|
|
33
|
+
loadFromRegistries(activeArg: TEnvelope | null, optionsArg?: IAppStoreCatalogLoadOptions): Promise<TAppStoreCatalogLoadResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Judges an envelope an operator imported (an offline signed bundle, or the envelope a package
|
|
36
|
+
* carries at `./catalog.envelope.json`) exactly as a fetched one: authentication, contract,
|
|
37
|
+
* schema, digests, then succession against the active catalog.
|
|
38
|
+
*/
|
|
39
|
+
judgeImportedEnvelope(candidateArg: unknown, activeArg: TEnvelope | null): Promise<TAppStoreCatalogJudgement>;
|
|
40
|
+
/** An envelope read as text: bytes that are not JSON are refused as `schemaInvalid`. */
|
|
41
|
+
judgeImportedEnvelopeText(textArg: string, activeArg: TEnvelope | null): Promise<TAppStoreCatalogJudgement>;
|
|
42
|
+
/**
|
|
43
|
+
* Restores the last-known-good catalog a consumer stored, at start: the stored envelope text is
|
|
44
|
+
* verified again against today's trusted keys and supported features, exactly as a fetched one,
|
|
45
|
+
* and answers the status `active` member it activates (`origin` and `loadedAt` as stored, the
|
|
46
|
+
* verifying key as verified now) or the refusal that keeps it inactive, e.g. `signatureInvalid`
|
|
47
|
+
* once its key is no longer trusted.
|
|
48
|
+
*/
|
|
49
|
+
restoreEnvelopeText(textArg: string, recordArg: IAppStoreCatalogStoredRecord): Promise<TAppStoreCatalogRestoreResult>;
|
|
50
|
+
private judgeText;
|
|
51
|
+
/** Envelope text as a value; bytes that are not JSON are a `schemaInvalid` refusal. */
|
|
52
|
+
private parseEnvelopeText;
|
|
53
|
+
private judge;
|
|
54
|
+
/**
|
|
55
|
+
* Authenticates and reads a candidate (`verifyAppStoreCatalogEnvelope`), before and without
|
|
56
|
+
* succession. The verifier answers every refusal as a result; should it throw, the candidate is
|
|
57
|
+
* refused as `schemaInvalid` with the cause, so a load never fails on an input it could not read.
|
|
58
|
+
*/
|
|
59
|
+
private verify;
|
|
60
|
+
/** The envelope text the package the registry's `latest` dist-tag names carries. */
|
|
61
|
+
private fetchEnvelopeText;
|
|
62
|
+
/**
|
|
63
|
+
* GET with the request bound covering the response body and a byte bound on the body. The
|
|
64
|
+
* caller's `signalArg` aborts it too; that abort is rethrown as the signal's reason. A redirect is
|
|
65
|
+
* never followed: a registry that answers with one fails, so a request never leaves https.
|
|
66
|
+
*/
|
|
67
|
+
private fetchBytes;
|
|
68
|
+
}
|
|
69
|
+
export {};
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { appStoreCatalogEnvelopePackagePath, appStoreCatalogPackageName, } from './catalog.js';
|
|
3
|
+
const defaultRequestTimeoutMs = 30_000;
|
|
4
|
+
const defaultMaximumTarballBytes = 32 * 1024 * 1024;
|
|
5
|
+
const maximumManifestBytes = 1024 * 1024;
|
|
6
|
+
const maximumEnvelopeBytes = 16 * 1024 * 1024;
|
|
7
|
+
const defaultMaximumUnpackedBytes = 256 * 1024 * 1024;
|
|
8
|
+
const envelopeTarballEntryName = `package/${appStoreCatalogEnvelopePackagePath}`;
|
|
9
|
+
/** A failure of the transport, the integrity check or the package layout: no candidate was read. */
|
|
10
|
+
class AppStoreCatalogTransportError extends Error {
|
|
11
|
+
}
|
|
12
|
+
const isRecord = (valueArg) => typeof valueArg === 'object' && valueArg !== null && !Array.isArray(valueArg);
|
|
13
|
+
const isPositiveSafeInteger = (valueArg) => Number.isSafeInteger(valueArg) && valueArg > 0;
|
|
14
|
+
const toBase64 = (bytesArg) => {
|
|
15
|
+
let binary = '';
|
|
16
|
+
for (const byte of bytesArg)
|
|
17
|
+
binary += String.fromCharCode(byte);
|
|
18
|
+
return btoa(binary);
|
|
19
|
+
};
|
|
20
|
+
/** The `sha512-<base64>` values of an npm `dist.integrity` (Subresource Integrity syntax). */
|
|
21
|
+
const readSha512Integrity = (integrityArg) => integrityArg.split(/\s+/)
|
|
22
|
+
.filter((entryArg) => entryArg.startsWith('sha512-'))
|
|
23
|
+
.map((entryArg) => entryArg.slice('sha512-'.length).replace(/\?.*$/, ''));
|
|
24
|
+
/**
|
|
25
|
+
* Loads the signed App Store catalog from npm registries and judges it against the consumer's
|
|
26
|
+
* active catalog. It holds the consumer's options only: no timer, no schedule, no catalog.
|
|
27
|
+
*
|
|
28
|
+
* One load asks every configured registry for the package its `latest` dist-tag names, downloads
|
|
29
|
+
* the tarball, checks it against the registry's `dist.integrity`, extracts the envelope, verifies
|
|
30
|
+
* it against the trusted keys (`verifyAppStoreCatalogEnvelope`) and judges its succession
|
|
31
|
+
* (`judgeAppStoreCatalogSuccession`). The first registry whose catalog activates ends the load;
|
|
32
|
+
* otherwise the result is the first `unchanged`, else the first `refused`, else `failed`, and
|
|
33
|
+
* `registries` reports what each registry answered.
|
|
34
|
+
*
|
|
35
|
+
* The loader holds no state, runs no schedule and persists nothing: the consumer's catalog runtime
|
|
36
|
+
* owns the one refresh schedule of its process, passes its active envelope in, stores an activated
|
|
37
|
+
* one as its last-known-good catalog and restores it with `restoreEnvelopeText` at start. Every
|
|
38
|
+
* request is bounded in time and size; a transport, integrity or packaging failure and every
|
|
39
|
+
* refusal are results, never thrown. Aborting `signal` (the consumer stopping) ends the load at
|
|
40
|
+
* once and rejects it with the signal's reason; no attempt is reported for it.
|
|
41
|
+
*/
|
|
42
|
+
export class AppStoreCatalogLoader {
|
|
43
|
+
trustedKeys;
|
|
44
|
+
supportedFeatureIds;
|
|
45
|
+
registries;
|
|
46
|
+
fetchFunction;
|
|
47
|
+
now;
|
|
48
|
+
requestTimeoutMs;
|
|
49
|
+
maximumTarballBytes;
|
|
50
|
+
maximumUnpackedBytes;
|
|
51
|
+
constructor(optionsArg) {
|
|
52
|
+
if (optionsArg.trustedKeys.length === 0) {
|
|
53
|
+
throw new Error('App Store catalog loader needs at least one trusted key');
|
|
54
|
+
}
|
|
55
|
+
this.trustedKeys = optionsArg.trustedKeys.map((keyArg) => ({ ...keyArg }));
|
|
56
|
+
this.supportedFeatureIds = [...new Set(optionsArg.supportedFeatureIds)].sort();
|
|
57
|
+
this.registries = plugins.servezoneInterfaces.appstore.resolveAppStoreCatalogRegistries(optionsArg.registries);
|
|
58
|
+
this.fetchFunction = optionsArg.fetch ?? ((inputArg, initArg) => fetch(inputArg, initArg));
|
|
59
|
+
this.now = optionsArg.now ?? (() => new Date());
|
|
60
|
+
this.requestTimeoutMs = optionsArg.requestTimeoutMs ?? defaultRequestTimeoutMs;
|
|
61
|
+
this.maximumTarballBytes = optionsArg.maximumTarballBytes ?? defaultMaximumTarballBytes;
|
|
62
|
+
this.maximumUnpackedBytes = optionsArg.maximumUnpackedBytes ?? defaultMaximumUnpackedBytes;
|
|
63
|
+
if (!isPositiveSafeInteger(this.requestTimeoutMs)) {
|
|
64
|
+
throw new Error('App Store catalog requestTimeoutMs must be a positive integer');
|
|
65
|
+
}
|
|
66
|
+
if (!isPositiveSafeInteger(this.maximumTarballBytes)) {
|
|
67
|
+
throw new Error('App Store catalog maximumTarballBytes must be a positive integer');
|
|
68
|
+
}
|
|
69
|
+
if (!isPositiveSafeInteger(this.maximumUnpackedBytes)) {
|
|
70
|
+
throw new Error('App Store catalog maximumUnpackedBytes must be a positive integer');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** One load from the configured registries; see the class description. */
|
|
74
|
+
async loadFromRegistries(activeArg, optionsArg = {}) {
|
|
75
|
+
const { signal } = optionsArg;
|
|
76
|
+
const reports = [];
|
|
77
|
+
const judgements = [];
|
|
78
|
+
for (const registry of this.registries) {
|
|
79
|
+
signal?.throwIfAborted();
|
|
80
|
+
try {
|
|
81
|
+
const { packageVersion, envelopeText } = await this.fetchEnvelopeText(registry, signal);
|
|
82
|
+
const judgement = await this.judgeText(envelopeText, activeArg, 'registry');
|
|
83
|
+
judgements.push(judgement);
|
|
84
|
+
reports.push({
|
|
85
|
+
registry,
|
|
86
|
+
outcome: judgement.outcome,
|
|
87
|
+
packageVersion,
|
|
88
|
+
...(judgement.outcome === 'refused' ? { message: judgement.refused.message } : {}),
|
|
89
|
+
});
|
|
90
|
+
if (judgement.outcome === 'activated')
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
signal?.throwIfAborted();
|
|
95
|
+
if (!(error instanceof AppStoreCatalogTransportError))
|
|
96
|
+
throw error;
|
|
97
|
+
reports.push({ registry, outcome: 'failed', message: error.message });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
signal?.throwIfAborted();
|
|
101
|
+
const chosen = judgements.find((judgementArg) => judgementArg.outcome === 'activated')
|
|
102
|
+
?? judgements.find((judgementArg) => judgementArg.outcome === 'unchanged')
|
|
103
|
+
?? judgements.find((judgementArg) => judgementArg.outcome === 'refused');
|
|
104
|
+
if (chosen !== undefined)
|
|
105
|
+
return { ...chosen, registries: reports };
|
|
106
|
+
const message = reports.map((reportArg) => `${reportArg.registry}: ${reportArg.message ?? reportArg.outcome}`).join('; ');
|
|
107
|
+
const failure = {
|
|
108
|
+
outcome: 'failed',
|
|
109
|
+
message,
|
|
110
|
+
attempt: { at: this.now().toISOString(), outcome: 'failed', message },
|
|
111
|
+
};
|
|
112
|
+
return { ...failure, registries: reports };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Judges an envelope an operator imported (an offline signed bundle, or the envelope a package
|
|
116
|
+
* carries at `./catalog.envelope.json`) exactly as a fetched one: authentication, contract,
|
|
117
|
+
* schema, digests, then succession against the active catalog.
|
|
118
|
+
*/
|
|
119
|
+
async judgeImportedEnvelope(candidateArg, activeArg) {
|
|
120
|
+
return await this.judge(candidateArg, activeArg, 'operatorImport');
|
|
121
|
+
}
|
|
122
|
+
/** An envelope read as text: bytes that are not JSON are refused as `schemaInvalid`. */
|
|
123
|
+
async judgeImportedEnvelopeText(textArg, activeArg) {
|
|
124
|
+
return await this.judgeText(textArg, activeArg, 'operatorImport');
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Restores the last-known-good catalog a consumer stored, at start: the stored envelope text is
|
|
128
|
+
* verified again against today's trusted keys and supported features, exactly as a fetched one,
|
|
129
|
+
* and answers the status `active` member it activates (`origin` and `loadedAt` as stored, the
|
|
130
|
+
* verifying key as verified now) or the refusal that keeps it inactive, e.g. `signatureInvalid`
|
|
131
|
+
* once its key is no longer trusted.
|
|
132
|
+
*/
|
|
133
|
+
async restoreEnvelopeText(textArg, recordArg) {
|
|
134
|
+
const candidate = this.parseEnvelopeText(textArg);
|
|
135
|
+
const verified = candidate.parsed
|
|
136
|
+
? await this.verify(candidate.value, recordArg.origin)
|
|
137
|
+
: refuseCandidate(undefined, this.now().toISOString(), candidate.refusal);
|
|
138
|
+
if (verified.outcome === 'refused')
|
|
139
|
+
return verified;
|
|
140
|
+
return {
|
|
141
|
+
outcome: 'restored',
|
|
142
|
+
envelope: verified.envelope,
|
|
143
|
+
active: { ...verified.active, loadedAt: recordArg.loadedAt },
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
async judgeText(textArg, activeArg, originArg) {
|
|
147
|
+
const candidate = this.parseEnvelopeText(textArg);
|
|
148
|
+
return candidate.parsed
|
|
149
|
+
? await this.judge(candidate.value, activeArg, originArg)
|
|
150
|
+
: refuseCandidate(undefined, this.now().toISOString(), candidate.refusal);
|
|
151
|
+
}
|
|
152
|
+
/** Envelope text as a value; bytes that are not JSON are a `schemaInvalid` refusal. */
|
|
153
|
+
parseEnvelopeText(textArg) {
|
|
154
|
+
try {
|
|
155
|
+
return { parsed: true, value: JSON.parse(textArg) };
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return { parsed: false, refusal: { reason: 'schemaInvalid', message: 'envelope is not JSON' } };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async judge(candidateArg, activeArg, originArg) {
|
|
162
|
+
const verified = await this.verify(candidateArg, originArg);
|
|
163
|
+
if (verified.outcome === 'refused')
|
|
164
|
+
return verified;
|
|
165
|
+
const succession = plugins.servezoneInterfaces.appstore.judgeAppStoreCatalogSuccession(activeArg, verified.envelope);
|
|
166
|
+
if (succession.outcome === 'refused')
|
|
167
|
+
return refuseCandidate(candidateArg, verified.attempt.at, succession.refusal);
|
|
168
|
+
if (succession.outcome === 'unchanged') {
|
|
169
|
+
return { outcome: 'unchanged', attempt: { at: verified.attempt.at, outcome: 'unchanged' } };
|
|
170
|
+
}
|
|
171
|
+
return verified;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Authenticates and reads a candidate (`verifyAppStoreCatalogEnvelope`), before and without
|
|
175
|
+
* succession. The verifier answers every refusal as a result; should it throw, the candidate is
|
|
176
|
+
* refused as `schemaInvalid` with the cause, so a load never fails on an input it could not read.
|
|
177
|
+
*/
|
|
178
|
+
async verify(candidateArg, originArg) {
|
|
179
|
+
const at = this.now().toISOString();
|
|
180
|
+
let verification;
|
|
181
|
+
try {
|
|
182
|
+
verification = await plugins.servezoneInterfacesRuntime.verifyAppStoreCatalogEnvelope(candidateArg, {
|
|
183
|
+
trustedKeys: this.trustedKeys,
|
|
184
|
+
supportedFeatureIds: this.supportedFeatureIds,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return refuseCandidate(candidateArg, at, {
|
|
189
|
+
reason: 'schemaInvalid',
|
|
190
|
+
message: `envelope could not be verified: ${error instanceof Error ? error.message : String(error)}`,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
if (!verification.accepted)
|
|
194
|
+
return refuseCandidate(candidateArg, at, verification.refusal);
|
|
195
|
+
const { envelope, keyId } = verification;
|
|
196
|
+
const { catalog, catalogDigest } = envelope;
|
|
197
|
+
return {
|
|
198
|
+
outcome: 'activated',
|
|
199
|
+
envelope,
|
|
200
|
+
active: {
|
|
201
|
+
contract: { ...catalog.contract },
|
|
202
|
+
revision: catalog.revision,
|
|
203
|
+
catalogDigest,
|
|
204
|
+
publishedAt: catalog.publishedAt,
|
|
205
|
+
sourceRevision: catalog.sourceRevision,
|
|
206
|
+
loadedAt: at,
|
|
207
|
+
keyId,
|
|
208
|
+
origin: originArg,
|
|
209
|
+
},
|
|
210
|
+
attempt: { at, outcome: 'activated' },
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/** The envelope text the package the registry's `latest` dist-tag names carries. */
|
|
214
|
+
async fetchEnvelopeText(registryArg, signalArg) {
|
|
215
|
+
const manifestUrl = `${registryArg}/${appStoreCatalogPackageName.replace('/', '%2f')}/latest`;
|
|
216
|
+
const manifestBytes = await this.fetchBytes(manifestUrl, maximumManifestBytes, signalArg);
|
|
217
|
+
let manifest;
|
|
218
|
+
try {
|
|
219
|
+
manifest = JSON.parse(new TextDecoder().decode(manifestBytes));
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
throw new AppStoreCatalogTransportError(`${manifestUrl} did not answer JSON`);
|
|
223
|
+
}
|
|
224
|
+
const dist = isRecord(manifest) ? manifest.dist : undefined;
|
|
225
|
+
if (!isRecord(manifest) || manifest.name !== appStoreCatalogPackageName || typeof manifest.version !== 'string'
|
|
226
|
+
|| !isRecord(dist) || typeof dist.tarball !== 'string' || typeof dist.integrity !== 'string') {
|
|
227
|
+
throw new AppStoreCatalogTransportError(`${manifestUrl} is not the ${appStoreCatalogPackageName} version manifest`);
|
|
228
|
+
}
|
|
229
|
+
const packageVersion = manifest.version;
|
|
230
|
+
const expectedSha512 = readSha512Integrity(dist.integrity);
|
|
231
|
+
if (expectedSha512.length === 0) {
|
|
232
|
+
throw new AppStoreCatalogTransportError(`${appStoreCatalogPackageName}@${packageVersion} has no sha512 dist.integrity`);
|
|
233
|
+
}
|
|
234
|
+
let tarballUrl;
|
|
235
|
+
try {
|
|
236
|
+
tarballUrl = new URL(dist.tarball);
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
throw new AppStoreCatalogTransportError(`${appStoreCatalogPackageName}@${packageVersion} names no tarball URL`);
|
|
240
|
+
}
|
|
241
|
+
if (tarballUrl.protocol !== 'https:' || tarballUrl.username !== '' || tarballUrl.password !== '') {
|
|
242
|
+
throw new AppStoreCatalogTransportError(`${appStoreCatalogPackageName}@${packageVersion} tarball URL must be https without credentials`);
|
|
243
|
+
}
|
|
244
|
+
const tarball = await this.fetchBytes(tarballUrl.toString(), this.maximumTarballBytes, signalArg);
|
|
245
|
+
const actualSha512 = toBase64(new Uint8Array(await crypto.subtle.digest('SHA-512', tarball)));
|
|
246
|
+
if (!expectedSha512.includes(actualSha512)) {
|
|
247
|
+
throw new AppStoreCatalogTransportError(`${appStoreCatalogPackageName}@${packageVersion} tarball does not match its dist.integrity`);
|
|
248
|
+
}
|
|
249
|
+
const envelopeText = await readTarballEntry(tarball, envelopeTarballEntryName, packageVersion, this.maximumUnpackedBytes);
|
|
250
|
+
return { packageVersion, envelopeText };
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* GET with the request bound covering the response body and a byte bound on the body. The
|
|
254
|
+
* caller's `signalArg` aborts it too; that abort is rethrown as the signal's reason. A redirect is
|
|
255
|
+
* never followed: a registry that answers with one fails, so a request never leaves https.
|
|
256
|
+
*/
|
|
257
|
+
async fetchBytes(urlArg, maximumBytesArg, signalArg) {
|
|
258
|
+
const controller = new AbortController();
|
|
259
|
+
const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);
|
|
260
|
+
const signal = signalArg === undefined ? controller.signal : AbortSignal.any([controller.signal, signalArg]);
|
|
261
|
+
try {
|
|
262
|
+
const response = await this.fetchFunction(urlArg, {
|
|
263
|
+
signal,
|
|
264
|
+
redirect: 'error',
|
|
265
|
+
headers: { accept: 'application/json, application/octet-stream' },
|
|
266
|
+
});
|
|
267
|
+
if (!response.ok) {
|
|
268
|
+
await response.body?.cancel();
|
|
269
|
+
throw new AppStoreCatalogTransportError(`HTTP ${response.status} for ${urlArg}`);
|
|
270
|
+
}
|
|
271
|
+
const declaredLength = Number(response.headers.get('content-length') ?? '0');
|
|
272
|
+
if (declaredLength > maximumBytesArg) {
|
|
273
|
+
await response.body?.cancel();
|
|
274
|
+
throw new AppStoreCatalogTransportError(`${urlArg} exceeds ${maximumBytesArg} bytes`);
|
|
275
|
+
}
|
|
276
|
+
if (response.body === null)
|
|
277
|
+
throw new AppStoreCatalogTransportError(`${urlArg} answered no body`);
|
|
278
|
+
return await readBounded(response.body, maximumBytesArg, urlArg);
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
signalArg?.throwIfAborted();
|
|
282
|
+
if (controller.signal.aborted) {
|
|
283
|
+
throw new AppStoreCatalogTransportError(`${urlArg} timed out after ${this.requestTimeoutMs} ms`);
|
|
284
|
+
}
|
|
285
|
+
if (error instanceof AppStoreCatalogTransportError)
|
|
286
|
+
throw error;
|
|
287
|
+
throw new AppStoreCatalogTransportError(`${urlArg} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
288
|
+
}
|
|
289
|
+
finally {
|
|
290
|
+
clearTimeout(timer);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/** The refusal record and attempt of a candidate, with its revision when it can be read. */
|
|
295
|
+
const refuseCandidate = (candidateArg, atArg, refusalArg) => {
|
|
296
|
+
const revision = readCandidateRevision(candidateArg);
|
|
297
|
+
return {
|
|
298
|
+
outcome: 'refused',
|
|
299
|
+
refused: { ...refusalArg, at: atArg, ...(revision === undefined ? {} : { revision }) },
|
|
300
|
+
attempt: { at: atArg, outcome: 'refused', message: `${refusalArg.reason}: ${refusalArg.message}` },
|
|
301
|
+
};
|
|
302
|
+
};
|
|
303
|
+
const readCandidateRevision = (candidateArg) => {
|
|
304
|
+
const catalog = isRecord(candidateArg) ? candidateArg.catalog : undefined;
|
|
305
|
+
const revision = isRecord(catalog) ? catalog.revision : undefined;
|
|
306
|
+
return typeof revision === 'number' && Number.isSafeInteger(revision) ? revision : undefined;
|
|
307
|
+
};
|
|
308
|
+
const readBounded = async (streamArg, maximumBytesArg, labelArg) => {
|
|
309
|
+
const reader = streamArg.getReader();
|
|
310
|
+
const chunks = [];
|
|
311
|
+
let total = 0;
|
|
312
|
+
try {
|
|
313
|
+
for (;;) {
|
|
314
|
+
const { done, value } = await reader.read();
|
|
315
|
+
if (done)
|
|
316
|
+
break;
|
|
317
|
+
total += value.byteLength;
|
|
318
|
+
if (total > maximumBytesArg) {
|
|
319
|
+
await reader.cancel();
|
|
320
|
+
throw new AppStoreCatalogTransportError(`${labelArg} exceeds ${maximumBytesArg} bytes`);
|
|
321
|
+
}
|
|
322
|
+
chunks.push(value);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
finally {
|
|
326
|
+
reader.releaseLock();
|
|
327
|
+
}
|
|
328
|
+
const bytes = new Uint8Array(total);
|
|
329
|
+
let offset = 0;
|
|
330
|
+
for (const chunk of chunks) {
|
|
331
|
+
bytes.set(chunk, offset);
|
|
332
|
+
offset += chunk.byteLength;
|
|
333
|
+
}
|
|
334
|
+
return bytes;
|
|
335
|
+
};
|
|
336
|
+
/**
|
|
337
|
+
* The text of one regular file in a gzip-compressed npm tarball. The archive is decompressed in
|
|
338
|
+
* full (bounded) before it is decoded: decoding straight from the decompression stream stalls on
|
|
339
|
+
* entries larger than one stream chunk.
|
|
340
|
+
*/
|
|
341
|
+
const readTarballEntry = async (tarballArg, entryNameArg, packageVersionArg, maximumUnpackedBytesArg) => {
|
|
342
|
+
const label = `${appStoreCatalogPackageName}@${packageVersionArg}`;
|
|
343
|
+
try {
|
|
344
|
+
const archive = await readBounded(new Blob([tarballArg]).stream().pipeThrough(new DecompressionStream('gzip')), maximumUnpackedBytesArg, `${label} unpacked tarball`);
|
|
345
|
+
const entry = (await plugins.modernTar.unpackTar(archive, { strict: true }))
|
|
346
|
+
.find((entryArg) => entryArg.header.name === entryNameArg && (entryArg.header.type ?? 'file') === 'file');
|
|
347
|
+
if (entry?.data === undefined) {
|
|
348
|
+
throw new AppStoreCatalogTransportError(`${label} carries no ${appStoreCatalogEnvelopePackagePath}`);
|
|
349
|
+
}
|
|
350
|
+
if (entry.data.byteLength > maximumEnvelopeBytes) {
|
|
351
|
+
throw new AppStoreCatalogTransportError(`${label} ${appStoreCatalogEnvelopePackagePath} exceeds ${maximumEnvelopeBytes} bytes`);
|
|
352
|
+
}
|
|
353
|
+
return new TextDecoder().decode(entry.data);
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
if (error instanceof AppStoreCatalogTransportError)
|
|
357
|
+
throw error;
|
|
358
|
+
throw new AppStoreCatalogTransportError(`${label} tarball could not be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9hZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfY2xpZW50L2xvYWRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEVBQ0wsa0NBQWtDLEVBQ2xDLDBCQUEwQixHQUMzQixNQUFNLGNBQWMsQ0FBQztBQW1CdEIsTUFBTSx1QkFBdUIsR0FBRyxNQUFNLENBQUM7QUFDdkMsTUFBTSwwQkFBMEIsR0FBRyxFQUFFLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQztBQUNwRCxNQUFNLG9CQUFvQixHQUFHLElBQUksR0FBRyxJQUFJLENBQUM7QUFDekMsTUFBTSxvQkFBb0IsR0FBRyxFQUFFLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQztBQUM5QyxNQUFNLDJCQUEyQixHQUFHLEdBQUcsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDO0FBQ3RELE1BQU0sd0JBQXdCLEdBQUcsV0FBVyxrQ0FBa0MsRUFBRSxDQUFDO0FBRWpGLG9HQUFvRztBQUNwRyxNQUFNLDZCQUE4QixTQUFRLEtBQUs7Q0FBRztBQUVwRCxNQUFNLFFBQVEsR0FBRyxDQUFDLFFBQWlCLEVBQXVDLEVBQUUsQ0FDMUUsT0FBTyxRQUFRLEtBQUssUUFBUSxJQUFJLFFBQVEsS0FBSyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBRWhGLE1BQU0scUJBQXFCLEdBQUcsQ0FBQyxRQUFnQixFQUFXLEVBQUUsQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxJQUFJLFFBQVEsR0FBRyxDQUFDLENBQUM7QUFFNUcsTUFBTSxRQUFRLEdBQUcsQ0FBQyxRQUFvQixFQUFVLEVBQUU7SUFDaEQsSUFBSSxNQUFNLEdBQUcsRUFBRSxDQUFDO0lBQ2hCLEtBQUssTUFBTSxJQUFJLElBQUksUUFBUTtRQUFFLE1BQU0sSUFBSSxNQUFNLENBQUMsWUFBWSxD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
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// @serve.zone scope
|
|
2
|
+
import * as servezoneInterfaces from '@serve.zone/interfaces';
|
|
3
|
+
import * as servezoneInterfacesRuntime from '@serve.zone/interfaces/runtime';
|
|
4
|
+
export { servezoneInterfaces, servezoneInterfacesRuntime };
|
|
5
|
+
// third party
|
|
6
|
+
import * as modernTar from 'modern-tar';
|
|
7
|
+
export { modernTar };
|
|
8
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX2NsaWVudC9wbHVnaW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLG9CQUFvQjtBQUNwQixPQUFPLEtBQUssbUJBQW1CLE1BQU0sd0JBQXdCLENBQUM7QUFDOUQsT0FBTyxLQUFLLDBCQUEwQixNQUFNLGdDQUFnQyxDQUFDO0FBRTdFLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSwwQkFBMEIsRUFBRSxDQUFDO0FBRTNELGNBQWM7QUFDZCxPQUFPLEtBQUssU0FBUyxNQUFNLFlBQVksQ0FBQztBQUV4QyxPQUFPLEVBQUUsU0FBUyxFQUFFLENBQUMifQ==
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
type TCatalog = plugins.servezoneInterfaces.appstore.IAppStoreCatalog;
|
|
3
|
+
type TCatalogApp = plugins.servezoneInterfaces.appstore.IAppStoreCatalogApp;
|
|
4
|
+
type TAppStoreApp = plugins.servezoneInterfaces.appstore.IAppStoreApp;
|
|
5
|
+
type TAppStoreAppMeta = plugins.servezoneInterfaces.appstore.IAppStoreAppMeta;
|
|
6
|
+
type TVersionState = plugins.servezoneInterfaces.appstore.IAppStoreAppVersionState;
|
|
7
|
+
/** The consumer a projection is for: the feature ids it fulfils, and which host release it is. */
|
|
8
|
+
export interface IAppStoreCatalogProjectionOptions {
|
|
9
|
+
/** The `requiresFeatures` ids this consumer fulfils, e.g. `appStoreStorageFeatureIds` values. */
|
|
10
|
+
supportedFeatureIds: Iterable<string>;
|
|
11
|
+
/** The host and its release, judged against each version's `minOneboxVersion` / `minCloudlyVersion`. */
|
|
12
|
+
consumer: plugins.servezoneInterfaces.appstore.IAppStoreCatalogConsumer;
|
|
13
|
+
}
|
|
14
|
+
/** The App Store shapes Onebox and Cloudly carry in their views and requests for one app. */
|
|
15
|
+
export interface IAppStoreCatalogAppDisplay {
|
|
16
|
+
/** The listing entry (`getAppStoreTemplates`). */
|
|
17
|
+
app: TAppStoreApp;
|
|
18
|
+
/** The app metadata (`getAppStoreConfig`, the upgrade preview). */
|
|
19
|
+
meta: TAppStoreAppMeta;
|
|
20
|
+
}
|
|
21
|
+
/** One catalog app as a consumer with its supported feature ids sees it. */
|
|
22
|
+
export interface IAppStoreCatalogAppProjection {
|
|
23
|
+
appId: string;
|
|
24
|
+
/** The catalog's `latestVersion`, the publisher's latest, whether or not this consumer may install it. */
|
|
25
|
+
catalogLatestVersion: string;
|
|
26
|
+
/**
|
|
27
|
+
* Every listed version in catalog order, withdrawn, feature-gated and newer-release-gated ones
|
|
28
|
+
* included, each with `installRefusal` as `appstore.judgeAppStoreCatalogInstallTarget` answers it.
|
|
29
|
+
*/
|
|
30
|
+
versions: TVersionState[];
|
|
31
|
+
/**
|
|
32
|
+
* The newest version this consumer may install, or `null` when none is: every version withdrawn,
|
|
33
|
+
* gated on a feature it does not fulfil or requiring a newer release of it. Under `semver` the highest install target; under
|
|
34
|
+
* `branch` the catalog's `latestVersion` when it is one, since branch tokens have no order.
|
|
35
|
+
*/
|
|
36
|
+
latestInstallableVersion: string | null;
|
|
37
|
+
/**
|
|
38
|
+
* The display shapes, or `null` when `latestInstallableVersion` is: their `versions` list only
|
|
39
|
+
* install targets and their `latestVersion` names `latestInstallableVersion`, so a version
|
|
40
|
+
* picker built from them never offers a version the consumer refuses; their `versionStates`
|
|
41
|
+
* state every listed version with its refusal.
|
|
42
|
+
*/
|
|
43
|
+
display: IAppStoreCatalogAppDisplay | null;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Projects one catalog app for a consumer: every listed version with its install refusal, the
|
|
47
|
+
* newest install target for this consumer, and the `IAppStoreApp` / `IAppStoreAppMeta` shapes its App Store views
|
|
48
|
+
* show. Pure: it reads the catalog app and never changes it.
|
|
49
|
+
*/
|
|
50
|
+
export declare const projectAppStoreCatalogApp: (appArg: TCatalogApp, optionsArg: IAppStoreCatalogProjectionOptions) => IAppStoreCatalogAppProjection;
|
|
51
|
+
/** Projects every catalog app, in catalog order (by id). */
|
|
52
|
+
export declare const projectAppStoreCatalog: (catalogArg: TCatalog, optionsArg: IAppStoreCatalogProjectionOptions) => IAppStoreCatalogAppProjection[];
|
|
53
|
+
/**
|
|
54
|
+
* The App Store listing a consumer serves (`getAppStoreTemplates`): one `IAppStoreApp` per app
|
|
55
|
+
* that has an install target for it, in catalog order. An app without one is left out of the
|
|
56
|
+
* listing; its projection (`projectAppStoreCatalogApp`) still states why.
|
|
57
|
+
*/
|
|
58
|
+
export declare const getAppStoreCatalogListing: (catalogArg: TCatalog, optionsArg: IAppStoreCatalogProjectionOptions) => TAppStoreApp[];
|
|
59
|
+
export {};
|