packtory 0.0.9 → 0.0.10
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/artifacts/artifacts-builder.js +0 -75
- package/artifacts/artifacts-builder.js.map +1 -1
- package/bundle-emitter/emitter.js +0 -34
- package/bundle-emitter/emitter.js.map +1 -1
- package/bundle-emitter/extract-package-tarball.js +0 -11
- package/bundle-emitter/extract-package-tarball.js.map +1 -1
- package/bundle-emitter/registry-client.js +0 -261
- package/bundle-emitter/registry-client.js.map +1 -1
- package/bundle-emitter/repository-url-normalizer.js +0 -36
- package/bundle-emitter/repository-url-normalizer.js.map +1 -1
- package/checks/check-runner.js +0 -5
- package/checks/check-runner.js.map +1 -1
- package/checks/rule.js +0 -8
- package/checks/rule.js.map +1 -1
- package/checks/rules/max-bundle-size.js +0 -35
- package/checks/rules/max-bundle-size.js.map +1 -1
- package/checks/rules/no-dev-dependency-imports.js +0 -28
- package/checks/rules/no-dev-dependency-imports.js.map +1 -1
- package/checks/rules/no-duplicated-files.js +0 -107
- package/checks/rules/no-duplicated-files.js.map +1 -1
- package/checks/rules/no-side-effects.js +0 -42
- package/checks/rules/no-side-effects.js.map +1 -1
- package/checks/rules/no-unused-bundle-dependencies.js +0 -24
- package/checks/rules/no-unused-bundle-dependencies.js.map +1 -1
- package/checks/rules/registry.js +0 -9
- package/checks/rules/registry.js.map +1 -1
- package/checks/rules/required-files.js +0 -31
- package/checks/rules/required-files.js.map +1 -1
- package/checks/rules/unique-target-paths.js +0 -25
- package/checks/rules/unique-target-paths.js.map +1 -1
- package/config/additional-files.js +0 -4
- package/config/additional-files.js.map +1 -1
- package/config/automatic-versioning-settings.js +0 -4
- package/config/automatic-versioning-settings.js.map +1 -1
- package/config/config.js +0 -2
- package/config/config.js.map +1 -1
- package/config/dead-code-elimination-settings.d.ts +6 -0
- package/config/dead-code-elimination-settings.d.ts.map +1 -1
- package/config/dead-code-elimination-settings.js +1 -3
- package/config/dead-code-elimination-settings.js.map +1 -1
- package/config/dependency-policy.js +0 -3
- package/config/dependency-policy.js.map +1 -1
- package/config/main-package-json-schema.js +0 -1
- package/config/main-package-json-schema.js.map +1 -1
- package/config/manual-versioning-settings.js +0 -4
- package/config/manual-versioning-settings.js.map +1 -1
- package/config/package-config.js +0 -6
- package/config/package-config.js.map +1 -1
- package/config/package-interface.d.ts +2 -0
- package/config/package-interface.d.ts.map +1 -1
- package/config/package-interface.js +0 -23
- package/config/package-interface.js.map +1 -1
- package/config/package-json.d.ts +0 -1
- package/config/package-json.d.ts.map +1 -1
- package/config/package-json.js +0 -16
- package/config/package-json.js.map +1 -1
- package/config/packtory-config-schema.js +0 -3
- package/config/packtory-config-schema.js.map +1 -1
- package/config/publish-settings.js +0 -22
- package/config/publish-settings.js.map +1 -1
- package/config/registry-settings.d.ts +0 -20
- package/config/registry-settings.d.ts.map +1 -1
- package/config/registry-settings.js +0 -38
- package/config/registry-settings.js.map +1 -1
- package/config/root.js +0 -4
- package/config/root.js.map +1 -1
- package/config/sbom-settings.js +0 -3
- package/config/sbom-settings.js.map +1 -1
- package/config/validation.d.ts.map +1 -1
- package/config/validation.js +0 -232
- package/config/validation.js.map +1 -1
- package/config/versioning-settings.js +0 -1
- package/config/versioning-settings.js.map +1 -1
- package/dead-code-eliminator/analyzed-bundle.d.ts +2 -0
- package/dead-code-eliminator/analyzed-bundle.d.ts.map +1 -1
- package/dead-code-eliminator/analyzed-bundle.js +0 -7
- package/dead-code-eliminator/analyzed-bundle.js.map +1 -1
- package/dead-code-eliminator/eliminator.js +3 -2
- package/dead-code-eliminator/eliminator.js.map +1 -1
- package/dead-code-eliminator/load-bundle.js +7 -6
- package/dead-code-eliminator/load-bundle.js.map +1 -1
- package/dead-code-eliminator/reachability/impure-statements.js +2 -2
- package/dead-code-eliminator/reachability/impure-statements.js.map +1 -1
- package/dead-code-eliminator/reachability/reachability.js +3 -3
- package/dead-code-eliminator/reachability/reachability.js.map +1 -1
- package/dead-code-eliminator/side-effect-classifier.js +141 -24
- package/dead-code-eliminator/side-effect-classifier.js.map +1 -1
- package/dependency-scanner/dependency-graph.d.ts +0 -2
- package/dependency-scanner/dependency-graph.d.ts.map +1 -1
- package/dependency-scanner/dependency-graph.js +0 -73
- package/dependency-scanner/dependency-graph.js.map +1 -1
- package/dependency-scanner/external-dependencies.d.ts +0 -1
- package/dependency-scanner/external-dependencies.d.ts.map +1 -1
- package/dependency-scanner/external-dependencies.js +0 -16
- package/dependency-scanner/external-dependencies.js.map +1 -1
- package/dependency-scanner/scanner.js +0 -69
- package/dependency-scanner/scanner.js.map +1 -1
- package/dependency-scanner/source-file-references.js +0 -26
- package/dependency-scanner/source-file-references.js.map +1 -1
- package/dependency-scanner/source-map-file-locator.js +0 -27
- package/dependency-scanner/source-map-file-locator.js.map +1 -1
- package/dependency-scanner/typescript-file-host.js +0 -114
- package/dependency-scanner/typescript-file-host.js.map +1 -1
- package/dependency-scanner/typescript-project-analyzer.d.ts +0 -1
- package/dependency-scanner/typescript-project-analyzer.d.ts.map +1 -1
- package/dependency-scanner/typescript-project-analyzer.js +0 -52
- package/dependency-scanner/typescript-project-analyzer.js.map +1 -1
- package/directed-graph/graph.d.ts +0 -1
- package/directed-graph/graph.d.ts.map +1 -1
- package/directed-graph/graph.js +0 -229
- package/directed-graph/graph.js.map +1 -1
- package/file-manager/compare.js +0 -17
- package/file-manager/compare.js.map +1 -1
- package/file-manager/equal.js +0 -5
- package/file-manager/equal.js.map +1 -1
- package/file-manager/file-manager.js +0 -52
- package/file-manager/file-manager.js.map +1 -1
- package/file-manager/permissions.js +0 -26
- package/file-manager/permissions.js.map +1 -1
- package/linker/linker.js +0 -23
- package/linker/linker.js.map +1 -1
- package/linker/resource-graph.js +0 -25
- package/linker/resource-graph.js.map +1 -1
- package/linker/source-modifier/import-paths.js +0 -22
- package/linker/source-modifier/import-paths.js.map +1 -1
- package/linker/substitute-bundles.js +0 -79
- package/linker/substitute-bundles.js.map +1 -1
- package/linker/substituted-resource-graph.js +0 -72
- package/linker/substituted-resource-graph.js.map +1 -1
- package/package-surface/modules.js +12 -230
- package/package-surface/modules.js.map +1 -1
- package/package-surface/surface.d.ts +0 -2
- package/package-surface/surface.d.ts.map +1 -1
- package/package-surface/surface.js +0 -15
- package/package-surface/surface.js.map +1 -1
- package/package.json +41 -3
- package/packtory/map-config.js +0 -14
- package/packtory/map-config.js.map +1 -1
- package/packtory/normalize-paths.js +0 -24
- package/packtory/normalize-paths.js.map +1 -1
- package/packtory/package-processor-build.js +0 -72
- package/packtory/package-processor-build.js.map +1 -1
- package/packtory/package-processor-publish.d.ts +0 -11
- package/packtory/package-processor-publish.d.ts.map +1 -1
- package/packtory/package-processor-publish.js +0 -133
- package/packtory/package-processor-publish.js.map +1 -1
- package/packtory/package-processor.js +0 -8
- package/packtory/package-processor.js.map +1 -1
- package/packtory/packtory-resolve.js +16 -9
- package/packtory/packtory-resolve.js.map +1 -1
- package/packtory/packtory-results.js +0 -15
- package/packtory/packtory-results.js.map +1 -1
- package/packtory/prepare-package-options.d.ts +2 -14
- package/packtory/prepare-package-options.d.ts.map +1 -1
- package/packtory/prepare-package-options.js +1 -118
- package/packtory/prepare-package-options.js.map +1 -1
- package/packtory/resolved-package.js +0 -31
- package/packtory/resolved-package.js.map +1 -1
- package/progress/progress-broadcaster.js +0 -45
- package/progress/progress-broadcaster.js.map +1 -1
- package/report/artifact-entry-merger.js +0 -20
- package/report/artifact-entry-merger.js.map +1 -1
- package/report/inspectors.js +0 -73
- package/report/inspectors.js.map +1 -1
- package/report/report-aggregator.js +0 -185
- package/report/report-aggregator.js.map +1 -1
- package/resource-resolver/content.js +0 -53
- package/resource-resolver/content.js.map +1 -1
- package/resource-resolver/resource-resolve-options.js +0 -12
- package/resource-resolver/resource-resolve-options.js.map +1 -1
- package/resource-resolver/resource-resolver.js +0 -78
- package/resource-resolver/resource-resolver.js.map +1 -1
- package/sbom/extract-license.js +0 -10
- package/sbom/extract-license.js.map +1 -1
- package/sbom/license-resolver.js +0 -19
- package/sbom/license-resolver.js.map +1 -1
- package/sbom/sbom-file.js +0 -54
- package/sbom/sbom-file.js.map +1 -1
- package/sbom/sbom-serializer.js +0 -9
- package/sbom/sbom-serializer.js.map +1 -1
- package/sbom.cdx.json +390 -0
- package/tar/extract-tar.js +0 -38
- package/tar/extract-tar.js.map +1 -1
- package/version-manager/manager.js +0 -46
- package/version-manager/manager.js.map +1 -1
- package/version-manager/manifest/builder.js +0 -56
- package/version-manager/manifest/builder.js.map +1 -1
- package/version-manager/manifest/serialize.js +0 -46
- package/version-manager/manifest/serialize.js.map +1 -1
- package/version-manager/manifest/sort-values.js +0 -16
- package/version-manager/manifest/sort-values.js.map +1 -1
- package/version-manager/specifier-classifier.js +0 -33
- package/version-manager/specifier-classifier.js.map +1 -1
- package/version-manager/specifier-errors.js +0 -43
- package/version-manager/specifier-errors.js.map +1 -1
- package/version-manager/versioned-bundle-dependencies.js +0 -107
- package/version-manager/versioned-bundle-dependencies.js.map +1 -1
- package/version-manager/versioned-bundle-imports.d.ts +0 -1
- package/version-manager/versioned-bundle-imports.d.ts.map +1 -1
- package/version-manager/versioned-bundle-imports.js +0 -72
- package/version-manager/versioned-bundle-imports.js.map +1 -1
- package/version-manager/versioned-bundle.d.ts +0 -1
- package/version-manager/versioned-bundle.d.ts.map +1 -1
- package/version-manager/versioned-bundle.js +0 -60
- package/version-manager/versioned-bundle.js.map +1 -1
|
@@ -1,78 +1,3 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { inspectArtifactSizes } from "../report/inspectors.js";
|
|
3
|
-
export function createArtifactsBuilder(artifactsBuilderDependencies) {
|
|
4
|
-
const { fileManager, tarballBuilder, progressBroadcaster } = artifactsBuilderDependencies;
|
|
5
|
-
function applyPrefix(filePath, prefix) {
|
|
6
|
-
return prefix === undefined ? filePath : path.join(prefix, filePath);
|
|
7
|
-
}
|
|
8
|
-
function collectContents(bundle, prefix, extraFiles = []) {
|
|
9
|
-
const artifactContents = [
|
|
10
|
-
{
|
|
11
|
-
...bundle.manifestFile,
|
|
12
|
-
filePath: applyPrefix(bundle.manifestFile.filePath, prefix)
|
|
13
|
-
}
|
|
14
|
-
];
|
|
15
|
-
for (const entry of bundle.contents) {
|
|
16
|
-
artifactContents.push({
|
|
17
|
-
filePath: applyPrefix(entry.fileDescription.targetFilePath, prefix),
|
|
18
|
-
content: entry.fileDescription.content,
|
|
19
|
-
isExecutable: entry.fileDescription.isExecutable
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
for (const extraFile of extraFiles) {
|
|
23
|
-
artifactContents.push({
|
|
24
|
-
filePath: applyPrefix(extraFile.filePath, prefix),
|
|
25
|
-
content: extraFile.content,
|
|
26
|
-
isExecutable: extraFile.isExecutable
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
if (progressBroadcaster.hasSubscribers('artifactsCollected')) {
|
|
30
|
-
progressBroadcaster.emit('artifactsCollected', {
|
|
31
|
-
packageName: bundle.name,
|
|
32
|
-
entries: inspectArtifactSizes([
|
|
33
|
-
{
|
|
34
|
-
...bundle.manifestFile,
|
|
35
|
-
filePath: applyPrefix(bundle.manifestFile.filePath, prefix)
|
|
36
|
-
},
|
|
37
|
-
...bundle.contents.map((entry) => {
|
|
38
|
-
return {
|
|
39
|
-
filePath: applyPrefix(entry.fileDescription.targetFilePath, prefix),
|
|
40
|
-
content: entry.fileDescription.content,
|
|
41
|
-
isExecutable: entry.fileDescription.isExecutable,
|
|
42
|
-
sourceFilePath: entry.fileDescription.sourceFilePath,
|
|
43
|
-
isSubstituted: entry.isSubstituted
|
|
44
|
-
};
|
|
45
|
-
}),
|
|
46
|
-
...extraFiles.map((entry) => {
|
|
47
|
-
return {
|
|
48
|
-
...entry,
|
|
49
|
-
filePath: applyPrefix(entry.filePath, prefix)
|
|
50
|
-
};
|
|
51
|
-
})
|
|
52
|
-
])
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
return artifactContents;
|
|
56
|
-
}
|
|
57
|
-
return {
|
|
58
|
-
collectContents,
|
|
59
|
-
async buildTarball(bundle, extraFiles) {
|
|
60
|
-
const contents = collectContents(bundle, 'package', extraFiles);
|
|
61
|
-
const tarData = await tarballBuilder.build(contents);
|
|
62
|
-
return { tarData };
|
|
63
|
-
},
|
|
64
|
-
async buildFolder(bundle, targetFolder, extraFiles) {
|
|
65
|
-
const readability = await fileManager.checkReadability(targetFolder);
|
|
66
|
-
if (readability.isReadable) {
|
|
67
|
-
throw new Error(`Folder ${targetFolder} already exists`);
|
|
68
|
-
}
|
|
69
|
-
const contents = collectContents(bundle, undefined, extraFiles);
|
|
70
|
-
for (const entry of contents) {
|
|
71
|
-
const targetFilePath = path.join(targetFolder, entry.filePath);
|
|
72
|
-
await fileManager.writeFile(targetFilePath, entry.content);
|
|
73
|
-
await fileManager.setExecutable(targetFilePath, entry.isExecutable);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
3
|
//# sourceMappingURL=artifacts-builder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifacts-builder.js","sourceRoot":"","sources":["../../../../source/artifacts/artifacts-builder.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"artifacts-builder.js","sourceRoot":"","sources":["../../../../source/artifacts/artifacts-builder.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW;AAI5B,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB"}
|
|
@@ -3,38 +3,4 @@ import { Maybe } from 'true-myth';
|
|
|
3
3
|
import { compareFileDescriptions } from "../file-manager/compare.js";
|
|
4
4
|
import { extractPackageTarball } from "./extract-package-tarball.js";
|
|
5
5
|
import { assertRepositoryCoherence } from "./repository-coherence.js";
|
|
6
|
-
export function createBundleEmitter(dependencies) {
|
|
7
|
-
const { artifactsBuilder, registryClient, ciRepositoryUrl } = dependencies;
|
|
8
|
-
return {
|
|
9
|
-
async determineCurrentVersion(options) {
|
|
10
|
-
const { versioning, registrySettings, name } = options;
|
|
11
|
-
if (versioning.automatic) {
|
|
12
|
-
const latestVersion = await registryClient.fetchLatestVersion(name, registrySettings);
|
|
13
|
-
return latestVersion.map((version) => {
|
|
14
|
-
return version.version;
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
return Maybe.just(versioning.version);
|
|
18
|
-
},
|
|
19
|
-
async checkBundleAlreadyPublished(options) {
|
|
20
|
-
const { bundle, registrySettings, extraFiles } = options;
|
|
21
|
-
const latestVersion = await registryClient.fetchLatestVersion(bundle.name, registrySettings);
|
|
22
|
-
if (latestVersion.isNothing) {
|
|
23
|
-
return { alreadyPublishedAsLatest: false };
|
|
24
|
-
}
|
|
25
|
-
const artifactContents = artifactsBuilder.collectContents(bundle, 'package', extraFiles);
|
|
26
|
-
const tarball = await registryClient.fetchTarball(latestVersion.value.tarballUrl, registrySettings);
|
|
27
|
-
const latestVersionArtifactContents = await extractPackageTarball(tarball);
|
|
28
|
-
const result = compareFileDescriptions(artifactContents, latestVersionArtifactContents);
|
|
29
|
-
return { alreadyPublishedAsLatest: result.status === 'equal' };
|
|
30
|
-
},
|
|
31
|
-
async publish(options) {
|
|
32
|
-
if (options.publishSettings.access === 'public' && options.publishSettings.provenance?.type === 'auto') {
|
|
33
|
-
assertRepositoryCoherence(options.bundle.packageJson, ciRepositoryUrl);
|
|
34
|
-
}
|
|
35
|
-
const tarball = await artifactsBuilder.buildTarball(options.bundle, options.extraFiles);
|
|
36
|
-
await registryClient.publishPackage(options.bundle.packageJson, tarball.tarData, options.registrySettings, options.publishSettings);
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
6
|
//# sourceMappingURL=emitter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../../../source/bundle-emitter/emitter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../../../source/bundle-emitter/emitter.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW;AAMjC,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B;AAEpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B;AAEpE,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B"}
|
|
@@ -1,14 +1,3 @@
|
|
|
1
1
|
import { isExecutableFileMode } from "../file-manager/permissions.js";
|
|
2
2
|
import { extractTarEntries } from "../tar/extract-tar.js";
|
|
3
|
-
function tarEntryToFileDescription(tarEntry) {
|
|
4
|
-
return {
|
|
5
|
-
filePath: tarEntry.header.name,
|
|
6
|
-
content: tarEntry.content,
|
|
7
|
-
isExecutable: isExecutableFileMode(Number(tarEntry.header.mode))
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
export async function extractPackageTarball(tarball) {
|
|
11
|
-
const tarEntries = await extractTarEntries(tarball);
|
|
12
|
-
return tarEntries.map(tarEntryToFileDescription);
|
|
13
|
-
}
|
|
14
3
|
//# sourceMappingURL=extract-package-tarball.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract-package-tarball.js","sourceRoot":"","sources":["../../../../source/bundle-emitter/extract-package-tarball.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC
|
|
1
|
+
{"version":3,"file":"extract-package-tarball.js","sourceRoot":"","sources":["../../../../source/bundle-emitter/extract-package-tarball.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC;AACrE,OAAO,EAAE,iBAAiB,EAAiB,MAAM,uBAAuB"}
|
|
@@ -1,265 +1,4 @@
|
|
|
1
1
|
import { Maybe } from 'true-myth';
|
|
2
2
|
import { z } from 'zod/mini';
|
|
3
3
|
import { buildPublishOptionsForPublishSettings, remapPublishError } from "./publish-settings-bridge.js";
|
|
4
|
-
const notFoundStatusCode = 404;
|
|
5
|
-
const unauthorizedStatusCode = 401;
|
|
6
|
-
const forbiddenStatusCode = 403;
|
|
7
|
-
const npmRegistryUrl = 'https://registry.npmjs.org/';
|
|
8
|
-
const oidcExchangeRefreshThresholdInMilliseconds = 60_000;
|
|
9
|
-
const packageVersionDetailsSchema = z.object({
|
|
10
|
-
dist: z.object({
|
|
11
|
-
tarball: z.string()
|
|
12
|
-
})
|
|
13
|
-
});
|
|
14
|
-
const abbreviatedPackageResponseSchema = z.object({
|
|
15
|
-
name: z.string(),
|
|
16
|
-
'dist-tags': z.object({
|
|
17
|
-
latest: z.optional(z.string())
|
|
18
|
-
}),
|
|
19
|
-
versions: z.record(z.string(), packageVersionDetailsSchema)
|
|
20
|
-
});
|
|
21
|
-
const oidcExchangeResponseSchema = z.object({
|
|
22
|
-
token_type: z.string(),
|
|
23
|
-
token: z.string(),
|
|
24
|
-
created: z.string(),
|
|
25
|
-
expires: z.string()
|
|
26
|
-
});
|
|
27
|
-
function encodePackageName(name) {
|
|
28
|
-
return name.replace('/', '%2F');
|
|
29
|
-
}
|
|
30
|
-
function isRecord(value) {
|
|
31
|
-
return value instanceof Object;
|
|
32
|
-
}
|
|
33
|
-
function parseAbbreviatedPackageResponse(response) {
|
|
34
|
-
const result = abbreviatedPackageResponseSchema.safeParse(response);
|
|
35
|
-
return result.success ? result.data : undefined;
|
|
36
|
-
}
|
|
37
|
-
function toPublishManifest(manifest) {
|
|
38
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- libnpmpublish expects @npm/types PackageJson, which is structurally compatible with our validated manifest
|
|
39
|
-
return manifest;
|
|
40
|
-
}
|
|
41
|
-
function resolveRegistryUrl(registrySettings) {
|
|
42
|
-
return registrySettings.registryUrl;
|
|
43
|
-
}
|
|
44
|
-
function isNpmRegistry(registry) {
|
|
45
|
-
return new URL(registry ?? npmRegistryUrl).href === npmRegistryUrl;
|
|
46
|
-
}
|
|
47
|
-
function normalizeAuthConfig(registrySettings) {
|
|
48
|
-
if ('type' in registrySettings.auth) {
|
|
49
|
-
return {
|
|
50
|
-
publish: registrySettings.auth,
|
|
51
|
-
metadata: undefined
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
return {
|
|
55
|
-
publish: registrySettings.auth.publish,
|
|
56
|
-
metadata: registrySettings.auth.metadata
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
function resolvePublishAuth(registrySettings) {
|
|
60
|
-
return normalizeAuthConfig(registrySettings).publish;
|
|
61
|
-
}
|
|
62
|
-
function createBaseOptions(registrySettings) {
|
|
63
|
-
return {
|
|
64
|
-
alwaysAuth: true,
|
|
65
|
-
registry: resolveRegistryUrl(registrySettings)
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
function buildAuthOptions(auth, registrySettings) {
|
|
69
|
-
const registry = resolveRegistryUrl(registrySettings);
|
|
70
|
-
const options = createBaseOptions(registrySettings);
|
|
71
|
-
if (auth.type === 'bearer-token') {
|
|
72
|
-
return {
|
|
73
|
-
allowsAutomaticRetry: false,
|
|
74
|
-
registry,
|
|
75
|
-
options: {
|
|
76
|
-
...options,
|
|
77
|
-
forceAuth: {
|
|
78
|
-
token: auth.token
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
allowsAutomaticRetry: false,
|
|
85
|
-
registry,
|
|
86
|
-
options: {
|
|
87
|
-
...options,
|
|
88
|
-
forceAuth: {
|
|
89
|
-
_auth: Buffer.from(`${auth.username}:${auth.password}`).toString('base64')
|
|
90
|
-
},
|
|
91
|
-
...(auth.email === undefined ? {} : { email: auth.email })
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
function createAnonymousAuthResolution(registrySettings) {
|
|
96
|
-
return {
|
|
97
|
-
allowsAutomaticRetry: false,
|
|
98
|
-
registry: resolveRegistryUrl(registrySettings),
|
|
99
|
-
options: createBaseOptions(registrySettings)
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
function parseOidcExchangeResponse(response) {
|
|
103
|
-
const result = oidcExchangeResponseSchema.safeParse(response);
|
|
104
|
-
return result.success ? result.data : undefined;
|
|
105
|
-
}
|
|
106
|
-
async function parseJsonResponse(response) {
|
|
107
|
-
return response.json();
|
|
108
|
-
}
|
|
109
|
-
export function createRegistryClient(dependencies) {
|
|
110
|
-
const { npmFetch, publish, fetch: fetchImplementation, clock, promptForOneTimePassword, resolveIdToken } = dependencies;
|
|
111
|
-
const oidcExchangeTokenCache = new Map();
|
|
112
|
-
async function exchangeOidcToken(packageName, registrySettings, auth) {
|
|
113
|
-
const cacheKey = `${resolveRegistryUrl(registrySettings) ?? npmRegistryUrl}::${packageName}`;
|
|
114
|
-
const cachedToken = oidcExchangeTokenCache.get(cacheKey);
|
|
115
|
-
const currentTime = clock.getCurrentTimeInMilliseconds();
|
|
116
|
-
if (cachedToken !== undefined &&
|
|
117
|
-
cachedToken.expiresAt - currentTime > oidcExchangeRefreshThresholdInMilliseconds) {
|
|
118
|
-
return cachedToken.token;
|
|
119
|
-
}
|
|
120
|
-
if (!isNpmRegistry(resolveRegistryUrl(registrySettings))) {
|
|
121
|
-
throw new Error('npm-oidc auth is only supported with the npmjs.org registry');
|
|
122
|
-
}
|
|
123
|
-
const idToken = await resolveIdToken(auth);
|
|
124
|
-
const exchangeResponse = await fetchImplementation(`${npmRegistryUrl}-/npm/v1/oidc/token/exchange/package/${encodePackageName(packageName)}`, {
|
|
125
|
-
method: 'POST',
|
|
126
|
-
headers: { Authorization: `Bearer ${idToken}` }
|
|
127
|
-
});
|
|
128
|
-
if (!exchangeResponse.ok) {
|
|
129
|
-
throw new Error(`OIDC token exchange failed with status ${exchangeResponse.status}`);
|
|
130
|
-
}
|
|
131
|
-
const body = parseOidcExchangeResponse(await parseJsonResponse(exchangeResponse));
|
|
132
|
-
if (body === undefined) {
|
|
133
|
-
throw new TypeError('OIDC token exchange returned an invalid response');
|
|
134
|
-
}
|
|
135
|
-
const expiresAt = Date.parse(body.expires);
|
|
136
|
-
if (!Number.isFinite(expiresAt)) {
|
|
137
|
-
throw new TypeError('OIDC token exchange returned an invalid expiry timestamp');
|
|
138
|
-
}
|
|
139
|
-
oidcExchangeTokenCache.set(cacheKey, {
|
|
140
|
-
token: body.token,
|
|
141
|
-
expiresAt
|
|
142
|
-
});
|
|
143
|
-
return body.token;
|
|
144
|
-
}
|
|
145
|
-
async function resolveWriteAuthOptions(packageName, registrySettings) {
|
|
146
|
-
const auth = resolvePublishAuth(registrySettings);
|
|
147
|
-
if (auth.type !== 'npm-oidc') {
|
|
148
|
-
return buildAuthOptions(auth, registrySettings).options;
|
|
149
|
-
}
|
|
150
|
-
const token = await exchangeOidcToken(packageName, registrySettings, auth);
|
|
151
|
-
return {
|
|
152
|
-
...createBaseOptions(registrySettings),
|
|
153
|
-
forceAuth: {
|
|
154
|
-
token
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
function resolveMetadataAuthOptions(registrySettings) {
|
|
159
|
-
const { metadata: metadataMode, publish: publishAuth } = normalizeAuthConfig(registrySettings);
|
|
160
|
-
if (metadataMode === undefined || metadataMode === 'inherit-publish-auth') {
|
|
161
|
-
if (publishAuth.type === 'npm-oidc') {
|
|
162
|
-
return createAnonymousAuthResolution(registrySettings);
|
|
163
|
-
}
|
|
164
|
-
return buildAuthOptions(publishAuth, registrySettings);
|
|
165
|
-
}
|
|
166
|
-
if (metadataMode === 'auto') {
|
|
167
|
-
return {
|
|
168
|
-
...createAnonymousAuthResolution(registrySettings),
|
|
169
|
-
allowsAutomaticRetry: true
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
if (typeof metadataMode !== 'object') {
|
|
173
|
-
return createAnonymousAuthResolution(registrySettings);
|
|
174
|
-
}
|
|
175
|
-
return buildAuthOptions(metadataMode, registrySettings);
|
|
176
|
-
}
|
|
177
|
-
async function retryAutoMetadataAuth(registrySettings, auth, run) {
|
|
178
|
-
try {
|
|
179
|
-
return await run(auth.options);
|
|
180
|
-
}
|
|
181
|
-
catch (error) {
|
|
182
|
-
const statusCode = isRecord(error) ? error.statusCode : undefined;
|
|
183
|
-
if (!auth.allowsAutomaticRetry ||
|
|
184
|
-
(statusCode !== forbiddenStatusCode && statusCode !== unauthorizedStatusCode)) {
|
|
185
|
-
throw error;
|
|
186
|
-
}
|
|
187
|
-
const publishAuth = resolvePublishAuth(registrySettings);
|
|
188
|
-
if (publishAuth.type === 'npm-oidc') {
|
|
189
|
-
throw error;
|
|
190
|
-
}
|
|
191
|
-
return run(buildAuthOptions(publishAuth, registrySettings).options);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
async function fetchRegistryEndpoint(endpoint, registrySettings) {
|
|
195
|
-
const acceptHeaderForFetchingAbbreviatedResponse = 'application/vnd.npm.install-v1+json';
|
|
196
|
-
const auth = resolveMetadataAuthOptions(registrySettings);
|
|
197
|
-
return retryAutoMetadataAuth(registrySettings, auth, async (options) => {
|
|
198
|
-
return npmFetch.json(endpoint, {
|
|
199
|
-
...options,
|
|
200
|
-
headers: { accept: acceptHeaderForFetchingAbbreviatedResponse }
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
async function fetchPackage(packageName, registrySettings) {
|
|
205
|
-
const endpointUri = `/${encodePackageName(packageName)}`;
|
|
206
|
-
try {
|
|
207
|
-
const response = await fetchRegistryEndpoint(endpointUri, registrySettings);
|
|
208
|
-
const result = parseAbbreviatedPackageResponse(response);
|
|
209
|
-
if (result === undefined) {
|
|
210
|
-
throw new Error('Got an invalid response from registry API');
|
|
211
|
-
}
|
|
212
|
-
return Maybe.just(result);
|
|
213
|
-
}
|
|
214
|
-
catch (error) {
|
|
215
|
-
const statusCode = isRecord(error) ? error.statusCode : undefined;
|
|
216
|
-
if (statusCode === notFoundStatusCode || statusCode === forbiddenStatusCode) {
|
|
217
|
-
return Maybe.nothing();
|
|
218
|
-
}
|
|
219
|
-
throw error;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return {
|
|
223
|
-
async fetchTarball(tarballUrl, registrySettings) {
|
|
224
|
-
const auth = resolveMetadataAuthOptions(registrySettings);
|
|
225
|
-
const response = await retryAutoMetadataAuth(registrySettings, auth, async (options) => {
|
|
226
|
-
return npmFetch(tarballUrl, options);
|
|
227
|
-
});
|
|
228
|
-
return response.buffer();
|
|
229
|
-
},
|
|
230
|
-
async publishPackage(manifest, tarData, registrySettings, publishSettings) {
|
|
231
|
-
const authOptions = await resolveWriteAuthOptions(manifest.name, registrySettings);
|
|
232
|
-
const publishOptionsFromSettings = buildPublishOptionsForPublishSettings(publishSettings);
|
|
233
|
-
try {
|
|
234
|
-
await publish(toPublishManifest(manifest), tarData, {
|
|
235
|
-
defaultTag: 'latest',
|
|
236
|
-
...authOptions,
|
|
237
|
-
...publishOptionsFromSettings,
|
|
238
|
-
...(promptForOneTimePassword === undefined ? {} : { otpPrompt: promptForOneTimePassword })
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
catch (error) {
|
|
242
|
-
throw remapPublishError(error, publishSettings);
|
|
243
|
-
}
|
|
244
|
-
},
|
|
245
|
-
async fetchLatestVersion(packageName, registrySettings) {
|
|
246
|
-
const packageResponse = await fetchPackage(packageName, registrySettings);
|
|
247
|
-
if (packageResponse.isNothing) {
|
|
248
|
-
return Maybe.nothing();
|
|
249
|
-
}
|
|
250
|
-
const latestVersion = packageResponse.value['dist-tags'].latest;
|
|
251
|
-
if (latestVersion !== undefined) {
|
|
252
|
-
const versionData = packageResponse.value.versions[latestVersion];
|
|
253
|
-
if (versionData === undefined) {
|
|
254
|
-
throw new Error(`Version "${latestVersion}" for package "${packageName}" has no entry in the registry response`);
|
|
255
|
-
}
|
|
256
|
-
return Maybe.just({
|
|
257
|
-
version: latestVersion,
|
|
258
|
-
tarballUrl: versionData.dist.tarball
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
return Maybe.nothing();
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
4
|
//# sourceMappingURL=registry-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-client.js","sourceRoot":"","sources":["../../../../source/bundle-emitter/registry-client.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"registry-client.js","sourceRoot":"","sources":["../../../../source/bundle-emitter/registry-client.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,UAAU;AAU5B,OAAO,EAAE,qCAAqC,EAAE,iBAAiB,EAAE,MAAM,8BAA8B"}
|
|
@@ -1,38 +1,2 @@
|
|
|
1
1
|
import HostedGitInfo from 'hosted-git-info';
|
|
2
|
-
function isNonEmptyString(value) {
|
|
3
|
-
return typeof value === 'string' && value.length > 0;
|
|
4
|
-
}
|
|
5
|
-
function isRecord(value) {
|
|
6
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
7
|
-
}
|
|
8
|
-
function extractRawUrl(input) {
|
|
9
|
-
if (isNonEmptyString(input)) {
|
|
10
|
-
return input;
|
|
11
|
-
}
|
|
12
|
-
if (isRecord(input) && isNonEmptyString(input.url)) {
|
|
13
|
-
return input.url;
|
|
14
|
-
}
|
|
15
|
-
return undefined;
|
|
16
|
-
}
|
|
17
|
-
function manualNormalize(url) {
|
|
18
|
-
let normalized = url.startsWith('git+') ? url.slice('git+'.length) : url;
|
|
19
|
-
if (normalized.endsWith('.git')) {
|
|
20
|
-
normalized = normalized.slice(0, -'.git'.length);
|
|
21
|
-
}
|
|
22
|
-
if (normalized.endsWith('/')) {
|
|
23
|
-
normalized = normalized.slice(0, -1);
|
|
24
|
-
}
|
|
25
|
-
return normalized.toLowerCase();
|
|
26
|
-
}
|
|
27
|
-
export function normalizeRepositoryUrl(input) {
|
|
28
|
-
const rawUrl = extractRawUrl(input);
|
|
29
|
-
if (rawUrl === undefined) {
|
|
30
|
-
return undefined;
|
|
31
|
-
}
|
|
32
|
-
const hosted = HostedGitInfo.fromUrl(rawUrl);
|
|
33
|
-
if (hosted !== undefined) {
|
|
34
|
-
return manualNormalize(hosted.https({ noCommittish: true }));
|
|
35
|
-
}
|
|
36
|
-
return manualNormalize(rawUrl);
|
|
37
|
-
}
|
|
38
2
|
//# sourceMappingURL=repository-url-normalizer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository-url-normalizer.js","sourceRoot":"","sources":["../../../../source/bundle-emitter/repository-url-normalizer.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"repository-url-normalizer.js","sourceRoot":"","sources":["../../../../source/bundle-emitter/repository-url-normalizer.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,iBAAiB"}
|
package/checks/check-runner.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-runner.js","sourceRoot":"","sources":["../../../../source/checks/check-runner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB
|
|
1
|
+
{"version":3,"file":"check-runner.js","sourceRoot":"","sources":["../../../../source/checks/check-runner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB"}
|
package/checks/rule.js
CHANGED
|
@@ -2,12 +2,4 @@ import { z } from 'zod/mini';
|
|
|
2
2
|
import { nonEmptyStringSchema } from "../config/base-validations.js";
|
|
3
3
|
export const enabledOnlyGlobalSchema = z.strictObject({ enabled: z.boolean() });
|
|
4
4
|
export const emptyPerPackageSchema = z.strictObject({});
|
|
5
|
-
const pathAllowListShape = {
|
|
6
|
-
allowList: z.optional(z.readonly(z.array(nonEmptyStringSchema)))
|
|
7
|
-
};
|
|
8
|
-
export const pathAllowListGlobalSchema = z.strictObject({
|
|
9
|
-
enabled: z.boolean(),
|
|
10
|
-
...pathAllowListShape
|
|
11
|
-
});
|
|
12
|
-
export const pathAllowListPerPackageSchema = z.strictObject(pathAllowListShape);
|
|
13
5
|
//# sourceMappingURL=rule.js.map
|
package/checks/rule.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule.js","sourceRoot":"","sources":["../../../../source/checks/rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAoB,MAAM,UAAU
|
|
1
|
+
{"version":3,"file":"rule.js","sourceRoot":"","sources":["../../../../source/checks/rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAoB,MAAM,UAAU;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B;AAGpE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;AAC/E,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC"}
|
|
@@ -1,40 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod/mini';
|
|
2
2
|
const ruleName = 'maxBundleSize';
|
|
3
|
-
const byteLimitSchema = z.number().check(z.int(), z.nonnegative());
|
|
4
|
-
const globalSchema = z.strictObject({
|
|
5
|
-
enabled: z.boolean(),
|
|
6
|
-
bytes: z.optional(byteLimitSchema)
|
|
7
|
-
});
|
|
8
|
-
const perPackageSchema = z.strictObject({
|
|
9
|
-
bytes: z.optional(byteLimitSchema)
|
|
10
|
-
});
|
|
11
|
-
function bundleSizeBytes(bundle) {
|
|
12
|
-
let total = 0;
|
|
13
|
-
for (const resource of bundle.contents) {
|
|
14
|
-
total += Buffer.byteLength(resource.fileDescription.content);
|
|
15
|
-
}
|
|
16
|
-
return total;
|
|
17
|
-
}
|
|
18
|
-
function checkBundle(bundle, threshold) {
|
|
19
|
-
if (threshold === undefined) {
|
|
20
|
-
return [];
|
|
21
|
-
}
|
|
22
|
-
const size = bundleSizeBytes(bundle);
|
|
23
|
-
if (size <= threshold) {
|
|
24
|
-
return [];
|
|
25
|
-
}
|
|
26
|
-
return [`Package "${bundle.name}" exceeds the maximum bundle size: ${size} bytes (limit: ${threshold} bytes)`];
|
|
27
|
-
}
|
|
28
|
-
function run(params) {
|
|
29
|
-
const globalConfig = params.settings?.maxBundleSize;
|
|
30
|
-
if (globalConfig?.enabled !== true) {
|
|
31
|
-
return [];
|
|
32
|
-
}
|
|
33
|
-
return params.bundles.flatMap((bundle) => {
|
|
34
|
-
const override = params.perPackageSettings.get(bundle.name)?.maxBundleSize?.bytes;
|
|
35
|
-
return checkBundle(bundle, override ?? globalConfig.bytes);
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
3
|
export const maxBundleSizeRule = {
|
|
39
4
|
name: ruleName,
|
|
40
5
|
globalSchema,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"max-bundle-size.js","sourceRoot":"","sources":["../../../../../source/checks/rules/max-bundle-size.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,UAAU
|
|
1
|
+
{"version":3,"file":"max-bundle-size.js","sourceRoot":"","sources":["../../../../../source/checks/rules/max-bundle-size.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,UAAU;AAI5B,MAAM,QAAQ,GAAG,eAAe;AAgDhC,MAAM,CAAC,MAAM,iBAAiB,GAAyE;IACnG,IAAI,EAAE,QAAQ;IACd,YAAY;IACZ,gBAAgB;IAChB,GAAG;CACN"}
|
|
@@ -1,33 +1,5 @@
|
|
|
1
1
|
import { emptyPerPackageSchema, enabledOnlyGlobalSchema } from "../rule.js";
|
|
2
2
|
const ruleName = 'noDevDependencyImports';
|
|
3
|
-
function findLeakedDevDependencies(bundle, packageConfig) {
|
|
4
|
-
const mainPackageJson = packageConfig?.mainPackageJson;
|
|
5
|
-
if (mainPackageJson === undefined) {
|
|
6
|
-
return [];
|
|
7
|
-
}
|
|
8
|
-
const runtimeDependencyNames = new Set([
|
|
9
|
-
...Object.keys(mainPackageJson.dependencies ?? {}),
|
|
10
|
-
...Object.keys(mainPackageJson.peerDependencies ?? {})
|
|
11
|
-
]);
|
|
12
|
-
const devDependencyNames = new Set(Object.keys(mainPackageJson.devDependencies ?? {}));
|
|
13
|
-
return Array.from(bundle.externalDependencies.keys())
|
|
14
|
-
.filter((name) => {
|
|
15
|
-
return devDependencyNames.has(name) && !runtimeDependencyNames.has(name);
|
|
16
|
-
})
|
|
17
|
-
.map((name) => {
|
|
18
|
-
const reason = 'is only declared in devDependencies of the main package.json';
|
|
19
|
-
return `Package "${bundle.name}" imports "${name}" which ${reason}`;
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
function run(params) {
|
|
23
|
-
const globalConfig = params.settings?.noDevDependencyImports;
|
|
24
|
-
if (globalConfig?.enabled !== true) {
|
|
25
|
-
return [];
|
|
26
|
-
}
|
|
27
|
-
return params.bundles.flatMap((bundle) => {
|
|
28
|
-
return findLeakedDevDependencies(bundle, params.packageConfigs?.[bundle.name]);
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
3
|
export const noDevDependencyImportsRule = {
|
|
32
4
|
name: ruleName,
|
|
33
5
|
globalSchema: enabledOnlyGlobalSchema,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-dev-dependency-imports.js","sourceRoot":"","sources":["../../../../../source/checks/rules/no-dev-dependency-imports.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,qBAAqB,EACrB,uBAAuB,EAI1B,MAAM,YAAY
|
|
1
|
+
{"version":3,"file":"no-dev-dependency-imports.js","sourceRoot":"","sources":["../../../../../source/checks/rules/no-dev-dependency-imports.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,qBAAqB,EACrB,uBAAuB,EAI1B,MAAM,YAAY;AAEnB,MAAM,QAAQ,GAAG,wBAAwB;AAyCzC,MAAM,CAAC,MAAM,0BAA0B,GAAyE;IAC5G,IAAI,EAAE,QAAQ;IACd,YAAY,EAAE,uBAAuB;IACrC,gBAAgB,EAAE,qBAAqB;IACvC,GAAG;CACN"}
|