@go-to-k/cdkd 0.235.0 → 0.236.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 +17 -4
- package/dist/{asg-provider-Dd9Xze-T.js → asg-provider-Dbl1_mwT.js} +3 -2
- package/dist/{asg-provider-Dd9Xze-T.js.map → asg-provider-Dbl1_mwT.js.map} +1 -1
- package/dist/{aws-clients-pjPwZz1r.js → aws-clients-B15NAPbL.js} +1 -1
- package/dist/{aws-clients-pjPwZz1r.js.map → aws-clients-B15NAPbL.js.map} +1 -1
- package/dist/cli.js +372 -14
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-Cbi5IzWV.js → deploy-engine-DqAIXGA_.js} +65 -11
- package/dist/deploy-engine-DqAIXGA_.js.map +1 -0
- package/dist/{ec2-termination-protection-uvVnteGl.js → ec2-termination-protection-q-WGWgxa.js} +3 -827
- package/dist/ec2-termination-protection-q-WGWgxa.js.map +1 -0
- package/dist/expected-bucket-owner-BjEfUnkA.js +87 -0
- package/dist/expected-bucket-owner-BjEfUnkA.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/logger-BzO-joNR.js +828 -0
- package/dist/logger-BzO-joNR.js.map +1 -0
- package/package.json +1 -1
- package/dist/deploy-engine-Cbi5IzWV.js.map +0 -1
- package/dist/ec2-termination-protection-uvVnteGl.js.map +0 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { a as getLiveRenderer, d as generateResourceNameWithFallback, l as applyDefaultNameForFallback, n as getLogger, p as withStackName, u as generateResourceName } from "./logger-BzO-joNR.js";
|
|
2
|
+
import { E as SynthesisError, S as ResourceUpdateNotSupportedError, T as StateError, a as normalizeAwsTagsToCfn, b as ProvisioningError, c as AssetError, d as DependencyError, g as MacroExpansionError, h as LockError, i as matchesCdkPath, k as normalizeAwsError, l as CdkdError, n as isTerminationProtectionPropagationError, o as resolveExplicitPhysicalId, s as assertRegionMatch, t as disableInstanceApiTermination, x as ResourceTimeoutError } from "./ec2-termination-protection-q-WGWgxa.js";
|
|
3
|
+
import { r as getAwsClients } from "./aws-clients-B15NAPbL.js";
|
|
4
|
+
import { t as expectedOwnerParam } from "./expected-bucket-owner-BjEfUnkA.js";
|
|
3
5
|
import { randomUUID } from "node:crypto";
|
|
4
6
|
import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
5
7
|
import { CloudControlClient, CreateResourceCommand, DeleteResourceCommand, GetResourceCommand, GetResourceRequestStatusCommand, ListResourcesCommand, UpdateResourceCommand } from "@aws-sdk/client-cloudcontrol";
|
|
@@ -77,7 +79,11 @@ async function resolveBucketRegion(bucketName, opts = {}) {
|
|
|
77
79
|
...opts.credentials && { credentials: opts.credentials }
|
|
78
80
|
});
|
|
79
81
|
try {
|
|
80
|
-
|
|
82
|
+
const { expectedOwnerParam } = await import("./expected-bucket-owner-BjEfUnkA.js").then((n) => n.n);
|
|
83
|
+
return (await client.send(new GetBucketLocationCommand({
|
|
84
|
+
Bucket: bucketName,
|
|
85
|
+
...await expectedOwnerParam(client)
|
|
86
|
+
}))).LocationConstraint || "us-east-1";
|
|
81
87
|
} catch {
|
|
82
88
|
return opts.fallbackRegion ?? "us-east-1";
|
|
83
89
|
} finally {
|
|
@@ -1366,6 +1372,7 @@ async function uploadCfnTemplate(args) {
|
|
|
1366
1372
|
const key = `${MIGRATE_TMP_PREFIX}/${stackName}/${Date.now()}.${ext}`;
|
|
1367
1373
|
try {
|
|
1368
1374
|
await s3.send(new PutObjectCommand({
|
|
1375
|
+
...await expectedOwnerParam(s3),
|
|
1369
1376
|
Bucket: bucket,
|
|
1370
1377
|
Key: key,
|
|
1371
1378
|
Body: body,
|
|
@@ -1380,7 +1387,8 @@ async function uploadCfnTemplate(args) {
|
|
|
1380
1387
|
try {
|
|
1381
1388
|
await s3.send(new DeleteObjectCommand({
|
|
1382
1389
|
Bucket: bucket,
|
|
1383
|
-
Key: key
|
|
1390
|
+
Key: key,
|
|
1391
|
+
...await expectedOwnerParam(s3)
|
|
1384
1392
|
}));
|
|
1385
1393
|
} finally {
|
|
1386
1394
|
s3.destroy();
|
|
@@ -1928,7 +1936,7 @@ async function resolveStateBucketWithDefaultAndSource(cliBucket, region) {
|
|
|
1928
1936
|
logger.debug("No state bucket specified, resolving default from account...");
|
|
1929
1937
|
const { GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
|
|
1930
1938
|
const { S3Client } = await import("@aws-sdk/client-s3");
|
|
1931
|
-
const { getAwsClients } = await import("./aws-clients-
|
|
1939
|
+
const { getAwsClients } = await import("./aws-clients-B15NAPbL.js").then((n) => n.n);
|
|
1932
1940
|
const accountId = (await getAwsClients().sts.send(new GetCallerIdentityCommand({}))).Account;
|
|
1933
1941
|
const newName = getDefaultStateBucketName(accountId);
|
|
1934
1942
|
const legacyName = getLegacyStateBucketName(accountId, region);
|
|
@@ -1986,11 +1994,13 @@ async function resolveStateBucketWithDefaultAndSource(cliBucket, region) {
|
|
|
1986
1994
|
*/
|
|
1987
1995
|
async function bucketHasAnyState(client, bucketName) {
|
|
1988
1996
|
const { ListObjectsV2Command } = await import("@aws-sdk/client-s3");
|
|
1997
|
+
const { expectedOwnerParam } = await import("./expected-bucket-owner-BjEfUnkA.js").then((n) => n.n);
|
|
1989
1998
|
try {
|
|
1990
1999
|
return ((await client.send(new ListObjectsV2Command({
|
|
1991
2000
|
Bucket: bucketName,
|
|
1992
2001
|
Prefix: "cdkd/",
|
|
1993
|
-
MaxKeys: 1
|
|
2002
|
+
MaxKeys: 1,
|
|
2003
|
+
...await expectedOwnerParam(client)
|
|
1994
2004
|
}))).KeyCount ?? 0) > 0;
|
|
1995
2005
|
} catch {
|
|
1996
2006
|
return true;
|
|
@@ -2011,8 +2021,12 @@ async function bucketHasAnyState(client, bucketName) {
|
|
|
2011
2021
|
*/
|
|
2012
2022
|
async function bucketExists(client, bucketName) {
|
|
2013
2023
|
const { HeadBucketCommand } = await import("@aws-sdk/client-s3");
|
|
2024
|
+
const { expectedOwnerParam } = await import("./expected-bucket-owner-BjEfUnkA.js").then((n) => n.n);
|
|
2014
2025
|
try {
|
|
2015
|
-
await client.send(new HeadBucketCommand({
|
|
2026
|
+
await client.send(new HeadBucketCommand({
|
|
2027
|
+
Bucket: bucketName,
|
|
2028
|
+
...await expectedOwnerParam(client)
|
|
2029
|
+
}));
|
|
2016
2030
|
return true;
|
|
2017
2031
|
} catch (error) {
|
|
2018
2032
|
const err = error;
|
|
@@ -4171,10 +4185,24 @@ var S3StateBackend = class {
|
|
|
4171
4185
|
* synthetic `UnknownError` (e.g. cross-region HEAD) becomes a concrete
|
|
4172
4186
|
* "Bucket does not exist" / "Access denied" / "different region" message.
|
|
4173
4187
|
*/
|
|
4188
|
+
/**
|
|
4189
|
+
* `ExpectedBucketOwner` spread for every state-bucket S3 call — S3 itself
|
|
4190
|
+
* rejects the call (403) when the bucket is owned by another account,
|
|
4191
|
+
* closing the predictable-name squatting hole (a foreign bucket that
|
|
4192
|
+
* ALLOWS this account would otherwise silently receive state
|
|
4193
|
+
* reads/writes). Best-effort: resolves to an empty object for
|
|
4194
|
+
* non-standard clients (test doubles) — see expected-bucket-owner.ts.
|
|
4195
|
+
*/
|
|
4196
|
+
async ownerParam() {
|
|
4197
|
+
return expectedOwnerParam(this.s3Client);
|
|
4198
|
+
}
|
|
4174
4199
|
async verifyBucketExists() {
|
|
4175
4200
|
await this.ensureClientForBucket();
|
|
4176
4201
|
try {
|
|
4177
|
-
await this.s3Client.send(new HeadBucketCommand({
|
|
4202
|
+
await this.s3Client.send(new HeadBucketCommand({
|
|
4203
|
+
Bucket: this.config.bucket,
|
|
4204
|
+
...await this.ownerParam()
|
|
4205
|
+
}));
|
|
4178
4206
|
} catch (error) {
|
|
4179
4207
|
const name = error.name;
|
|
4180
4208
|
if (name === "NotFound" || name === "NoSuchBucket") throw new StateError(`State bucket '${this.config.bucket}' does not exist. Run 'cdkd bootstrap' to create it, or specify an existing bucket via --state-bucket, CDKD_STATE_BUCKET, or cdk.json context.cdkd.stateBucket.`);
|
|
@@ -4221,6 +4249,7 @@ var S3StateBackend = class {
|
|
|
4221
4249
|
this.logger.debug(`Getting state for stack: ${stackName} (${region})`);
|
|
4222
4250
|
const response = await this.s3Client.send(new GetObjectCommand({
|
|
4223
4251
|
Bucket: this.config.bucket,
|
|
4252
|
+
...await this.ownerParam(),
|
|
4224
4253
|
Key: newKey
|
|
4225
4254
|
}));
|
|
4226
4255
|
if (!response.Body) throw new StateError(`State file for stack '${stackName}' (${region}) has no body`);
|
|
@@ -4283,6 +4312,7 @@ var S3StateBackend = class {
|
|
|
4283
4312
|
const bodyString = JSON.stringify(body, null, 2);
|
|
4284
4313
|
const response = await this.s3Client.send(new PutObjectCommand({
|
|
4285
4314
|
Bucket: this.config.bucket,
|
|
4315
|
+
...await this.ownerParam(),
|
|
4286
4316
|
Key: newKey,
|
|
4287
4317
|
Body: bodyString,
|
|
4288
4318
|
ContentLength: Buffer.byteLength(bodyString),
|
|
@@ -4294,6 +4324,7 @@ var S3StateBackend = class {
|
|
|
4294
4324
|
if (migrateLegacy) try {
|
|
4295
4325
|
await this.s3Client.send(new DeleteObjectCommand({
|
|
4296
4326
|
Bucket: this.config.bucket,
|
|
4327
|
+
...await this.ownerParam(),
|
|
4297
4328
|
Key: this.getLegacyStateKey(stackName)
|
|
4298
4329
|
}));
|
|
4299
4330
|
this.logger.info(`Migrated state for stack '${stackName}' to region-scoped layout (${region})`);
|
|
@@ -4323,11 +4354,13 @@ var S3StateBackend = class {
|
|
|
4323
4354
|
this.logger.debug(`Deleting state: ${stackName} (${region})`);
|
|
4324
4355
|
await this.s3Client.send(new DeleteObjectCommand({
|
|
4325
4356
|
Bucket: this.config.bucket,
|
|
4357
|
+
...await this.ownerParam(),
|
|
4326
4358
|
Key: this.getStateKey(stackName, region)
|
|
4327
4359
|
}));
|
|
4328
4360
|
if (await this.legacyMatchesRegion(stackName, region)) {
|
|
4329
4361
|
await this.s3Client.send(new DeleteObjectCommand({
|
|
4330
4362
|
Bucket: this.config.bucket,
|
|
4363
|
+
...await this.ownerParam(),
|
|
4331
4364
|
Key: this.getLegacyStateKey(stackName)
|
|
4332
4365
|
}));
|
|
4333
4366
|
this.logger.debug(`Deleted legacy state for stack: ${stackName}`);
|
|
@@ -4366,6 +4399,7 @@ var S3StateBackend = class {
|
|
|
4366
4399
|
do {
|
|
4367
4400
|
const response = await this.s3Client.send(new ListObjectsV2Command({
|
|
4368
4401
|
Bucket: this.config.bucket,
|
|
4402
|
+
...await this.ownerParam(),
|
|
4369
4403
|
Prefix: prefix,
|
|
4370
4404
|
...continuationToken && { ContinuationToken: continuationToken }
|
|
4371
4405
|
}));
|
|
@@ -4425,6 +4459,7 @@ var S3StateBackend = class {
|
|
|
4425
4459
|
await this.ensureClientForBucket();
|
|
4426
4460
|
await this.s3Client.send(new PutObjectCommand({
|
|
4427
4461
|
Bucket: this.config.bucket,
|
|
4462
|
+
...await this.ownerParam(),
|
|
4428
4463
|
Key: key,
|
|
4429
4464
|
Body: body,
|
|
4430
4465
|
ContentLength: Buffer.byteLength(body),
|
|
@@ -4440,6 +4475,7 @@ var S3StateBackend = class {
|
|
|
4440
4475
|
try {
|
|
4441
4476
|
return await (await this.s3Client.send(new GetObjectCommand({
|
|
4442
4477
|
Bucket: this.config.bucket,
|
|
4478
|
+
...await this.ownerParam(),
|
|
4443
4479
|
Key: key
|
|
4444
4480
|
}))).Body?.transformToString() ?? null;
|
|
4445
4481
|
} catch (error) {
|
|
@@ -4459,6 +4495,7 @@ var S3StateBackend = class {
|
|
|
4459
4495
|
do {
|
|
4460
4496
|
const response = await this.s3Client.send(new ListObjectsV2Command({
|
|
4461
4497
|
Bucket: this.config.bucket,
|
|
4498
|
+
...await this.ownerParam(),
|
|
4462
4499
|
Prefix: keyPrefix,
|
|
4463
4500
|
...continuationToken && { ContinuationToken: continuationToken }
|
|
4464
4501
|
}));
|
|
@@ -4489,6 +4526,7 @@ var S3StateBackend = class {
|
|
|
4489
4526
|
const chunk = keys.slice(i, i + 1e3);
|
|
4490
4527
|
const response = await this.s3Client.send(new DeleteObjectsCommand({
|
|
4491
4528
|
Bucket: this.config.bucket,
|
|
4529
|
+
...await this.ownerParam(),
|
|
4492
4530
|
Delete: {
|
|
4493
4531
|
Objects: chunk.map((Key) => ({ Key })),
|
|
4494
4532
|
Quiet: true
|
|
@@ -4506,6 +4544,7 @@ var S3StateBackend = class {
|
|
|
4506
4544
|
try {
|
|
4507
4545
|
await this.s3Client.send(new HeadObjectCommand({
|
|
4508
4546
|
Bucket: this.config.bucket,
|
|
4547
|
+
...await this.ownerParam(),
|
|
4509
4548
|
Key: key
|
|
4510
4549
|
}));
|
|
4511
4550
|
return true;
|
|
@@ -4523,6 +4562,7 @@ var S3StateBackend = class {
|
|
|
4523
4562
|
try {
|
|
4524
4563
|
const response = await this.s3Client.send(new GetObjectCommand({
|
|
4525
4564
|
Bucket: this.config.bucket,
|
|
4565
|
+
...await this.ownerParam(),
|
|
4526
4566
|
Key: this.getLegacyStateKey(stackName)
|
|
4527
4567
|
}));
|
|
4528
4568
|
if (!response.Body) return void 0;
|
|
@@ -4546,6 +4586,7 @@ var S3StateBackend = class {
|
|
|
4546
4586
|
try {
|
|
4547
4587
|
const response = await this.s3Client.send(new GetObjectCommand({
|
|
4548
4588
|
Bucket: this.config.bucket,
|
|
4589
|
+
...await this.ownerParam(),
|
|
4549
4590
|
Key: this.getLegacyStateKey(stackName)
|
|
4550
4591
|
}));
|
|
4551
4592
|
if (!response.Body || !response.ETag) return null;
|
|
@@ -4643,6 +4684,14 @@ var LockManager = class {
|
|
|
4643
4684
|
* so when the state backend has already resolved the same bucket this
|
|
4644
4685
|
* incurs no additional `GetBucketLocation` call.
|
|
4645
4686
|
*/
|
|
4687
|
+
/**
|
|
4688
|
+
* `ExpectedBucketOwner` spread for every state-bucket S3 call (squatting
|
|
4689
|
+
* hardening — see src/utils/expected-bucket-owner.ts). Best-effort:
|
|
4690
|
+
* resolves to an empty object for non-standard clients (test doubles).
|
|
4691
|
+
*/
|
|
4692
|
+
async ownerParam() {
|
|
4693
|
+
return expectedOwnerParam(this.s3Client);
|
|
4694
|
+
}
|
|
4646
4695
|
async ensureClientForBucket() {
|
|
4647
4696
|
if (this.clientResolved) return;
|
|
4648
4697
|
if (this.resolveInFlight) return this.resolveInFlight;
|
|
@@ -4732,6 +4781,7 @@ var LockManager = class {
|
|
|
4732
4781
|
const lockBody = JSON.stringify(lockInfo, null, 2);
|
|
4733
4782
|
await this.s3Client.send(new PutObjectCommand({
|
|
4734
4783
|
Bucket: this.config.bucket,
|
|
4784
|
+
...await this.ownerParam(),
|
|
4735
4785
|
Key: key,
|
|
4736
4786
|
Body: lockBody,
|
|
4737
4787
|
ContentLength: Buffer.byteLength(lockBody),
|
|
@@ -4751,6 +4801,7 @@ var LockManager = class {
|
|
|
4751
4801
|
const retryBody = JSON.stringify(lockInfo, null, 2);
|
|
4752
4802
|
await this.s3Client.send(new PutObjectCommand({
|
|
4753
4803
|
Bucket: this.config.bucket,
|
|
4804
|
+
...await this.ownerParam(),
|
|
4754
4805
|
Key: key,
|
|
4755
4806
|
Body: retryBody,
|
|
4756
4807
|
ContentLength: Buffer.byteLength(retryBody),
|
|
@@ -4786,6 +4837,7 @@ var LockManager = class {
|
|
|
4786
4837
|
this.logger.debug(`Getting lock info for stack: ${stackName}`);
|
|
4787
4838
|
const response = await this.s3Client.send(new GetObjectCommand({
|
|
4788
4839
|
Bucket: this.config.bucket,
|
|
4840
|
+
...await this.ownerParam(),
|
|
4789
4841
|
Key: key
|
|
4790
4842
|
}));
|
|
4791
4843
|
if (!response.Body) throw new LockError(`Lock file for stack '${stackName}' has no body`);
|
|
@@ -4823,6 +4875,7 @@ var LockManager = class {
|
|
|
4823
4875
|
this.logger.debug(`Releasing lock for stack: ${stackName} (${region})`);
|
|
4824
4876
|
await this.s3Client.send(new DeleteObjectCommand({
|
|
4825
4877
|
Bucket: this.config.bucket,
|
|
4878
|
+
...await this.ownerParam(),
|
|
4826
4879
|
Key: key
|
|
4827
4880
|
}));
|
|
4828
4881
|
this.logger.debug(`Lock released for stack: ${stackName} (${region})`);
|
|
@@ -4856,6 +4909,7 @@ var LockManager = class {
|
|
|
4856
4909
|
const key = this.getLockKey(stackName, region);
|
|
4857
4910
|
await this.s3Client.send(new DeleteObjectCommand({
|
|
4858
4911
|
Bucket: this.config.bucket,
|
|
4912
|
+
...await this.ownerParam(),
|
|
4859
4913
|
Key: key
|
|
4860
4914
|
}));
|
|
4861
4915
|
}
|
|
@@ -9527,7 +9581,7 @@ var CloudControlProvider = class {
|
|
|
9527
9581
|
this.logger.debug(`Deleting resource ${logicalId} (${resourceType}), physical ID: ${physicalId}`);
|
|
9528
9582
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
9529
9583
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
9530
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
9584
|
+
const { ASGProvider } = await import("./asg-provider-Dbl1_mwT.js").then((n) => n.n);
|
|
9531
9585
|
await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
|
|
9532
9586
|
return;
|
|
9533
9587
|
}
|
|
@@ -15843,5 +15897,5 @@ var DeployEngine = class {
|
|
|
15843
15897
|
};
|
|
15844
15898
|
|
|
15845
15899
|
//#endregion
|
|
15846
|
-
export {
|
|
15847
|
-
//# sourceMappingURL=deploy-engine-
|
|
15900
|
+
export { runDockerStreaming as $, DiffCalculator as A, buildAssetRedirectMap as B, findActionableSilentDrops as C, refStateLookupFromResource as D, cfnRefValueFromPhysicalId as E, rebuildClientForBucketRegion as F, BOOTSTRAP_MARKER_PREFIX as G, loadPublishableAssetManifest as H, shouldRetainResource as I, parseBootstrapMarker as J, ensureAssetStorage as K, AssetPublisher as L, TemplateParser as M, LockManager as N, WAFv2WebACLProvider as O, S3StateBackend as P, runDockerForeground as Q, stringifyValue as R, ProviderRegistry as S, IntrinsicFunctionResolver as T, rewriteTemplateAssetReferences as U, createAssetRedirectResolver as V, AssetModeResolver as W, formatDockerLoginError as X, buildDockerImage as Y, getDockerCmd as Z, green as _, findLargeInlineResources as _t, withRetry as a, getLegacyStateBucketName as at, IAMRoleProvider as b, clearBucketRegionCache as bt, computeImplicitDeleteEdges as c, resolveCaptureObservedState as ct, isStatefulRecreateTargetSync as d, resolveStateBucketWithDefaultAndSource as dt, AssetManifestLoader as et, renderStatefulReason as f, resolveUseCdkBootstrapAssets as ft, gray as g, MIGRATE_TMP_PREFIX as gt, cyan as h, CFN_TEMPLATE_URL_LIMIT as ht, withResourceDeadline as i, getDefaultStateBucketName as it, DagBuilder as j, applyRoleArnIfSet as k, extractDeploymentEventError as l, resolveSkipPrefix as lt, bold as m, CFN_TEMPLATE_BODY_LIMIT as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, Synthesizer as nt, isRetryableTransientError as o, resolveApp as ot, formatResourceLine as p, warnDeprecatedNoPrefixCliFlag as pt, getBootstrapMarkerKey as q, DeployEngine as r, synthesisStatusMessage as rt, IMPLICIT_DELETE_DEPENDENCIES as s, resolveAutoAssetStorage as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, getDockerImageBySourceHash as tt, MULTI_REGION_RECREATE_BLOCKED_TYPES as u, resolveStateBucketWithDefault as ut, red as v, uploadCfnTemplate as vt, CloudControlProvider as w, collectInlinePolicyNamesManagedBySiblings as x, resolveBucketRegion as xt, yellow as y, AssemblyReader as yt, WorkGraph as z };
|
|
15901
|
+
//# sourceMappingURL=deploy-engine-DqAIXGA_.js.map
|