cdk-local 0.162.1 → 0.163.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.
@@ -1,7 +1,8 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-w6R9maHv.js";
2
2
  import { c as getEmbedConfig, t as getLogger } from "./logger-Co5XVwco.js";
3
3
  import { r as displayUntrustedValue, u as describeCredentialLoadFailure } from "./assembly-path-R96uhMm1.js";
4
- import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
+ import { existsSync, lstatSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
5
+ import { createHash, generateKeyPairSync, randomBytes, sign } from "node:crypto";
5
6
  import { Agent, get, request } from "node:http";
6
7
  import { Agent as Agent$1, get as get$1, request as request$1 } from "node:https";
7
8
  import { rootCertificates } from "node:tls";
@@ -157,6 +158,9 @@ function setAwsEndpointOverride(o) {
157
158
  function getAwsEndpointOverride() {
158
159
  return endpointOverride;
159
160
  }
161
+ function awsEndpointOverrideClientConfig(o) {
162
+ return endpointOverrideClientConfig(o);
163
+ }
160
164
  function endpointOverrideClientConfig(o) {
161
165
  const httpsAgent = new Agent$1({
162
166
  keepAlive: true,
@@ -346,6 +350,543 @@ async function proxyAwareFetch(url, opts = {}) {
346
350
  }
347
351
  }
348
352
 
353
+ //#endregion
354
+ //#region src/local/aws-emulator-mixed-mode.ts
355
+ const AWS_EMULATOR_SENTINEL = "aws-emulator";
356
+ const MIXED_MODE_RESIGN_PORT = 47297;
357
+ const ENDPOINT_KEY = /^AWS_ENDPOINT_URL(?:_([A-Z0-9_]+))?$/;
358
+ function isAwsEmulatorSentinel(key, value) {
359
+ return typeof value === "string" && value.trim() === "aws-emulator" && ENDPOINT_KEY.test(key);
360
+ }
361
+ function findAwsEmulatorSentinels(overrides) {
362
+ const found = [];
363
+ if (!overrides || typeof overrides !== "object") return found;
364
+ for (const [section, map] of Object.entries(overrides)) {
365
+ if (!map || typeof map !== "object" || Array.isArray(map)) continue;
366
+ for (const [key, value] of Object.entries(map)) {
367
+ if (!isAwsEmulatorSentinel(key, value)) continue;
368
+ found.push({
369
+ section,
370
+ key,
371
+ service: ENDPOINT_KEY.exec(key)[1] ?? ""
372
+ });
373
+ }
374
+ }
375
+ return found;
376
+ }
377
+ function readAwsEmulatorSentinelsFromFile(filePath) {
378
+ if (!filePath) return [];
379
+ let parsed;
380
+ try {
381
+ parsed = JSON.parse(readFileSync(filePath, "utf8"));
382
+ } catch {
383
+ return [];
384
+ }
385
+ return findAwsEmulatorSentinels(parsed);
386
+ }
387
+ function validateAwsEmulatorSentinels(sentinels, awsEmulatorOn, envVarName = "CDK_LOCAL_AWS_EMULATOR") {
388
+ const first = sentinels[0];
389
+ if (!first) return;
390
+ const where = (s) => `${s.key} under ${displayUntrustedValue(s.section)}`;
391
+ const bare = sentinels.find((s) => s.service === "");
392
+ if (bare) throw new Error(`--env-vars sets ${where(bare)} to "${AWS_EMULATOR_SENTINEL}", but the sentinel routes ONE service (AWS_ENDPOINT_URL_<SERVICE>). To send every service to AWS Emulator, use --aws-emulator without a sentinel.`);
393
+ if (!awsEmulatorOn) throw new Error(`--env-vars sets ${where(first)} to "${AWS_EMULATOR_SENTINEL}", which needs --aws-emulator [url] (or ${envVarName}) to name the AWS Emulator to route it to.`);
394
+ }
395
+ function emulatedServicesOf(sentinels) {
396
+ return [...new Set(sentinels.map((s) => s.service).filter((s) => s !== ""))].sort();
397
+ }
398
+ const SERVICE_NAMESPACE_ALIASES = {
399
+ EVENTBRIDGE: ["EVENTS"],
400
+ SFN: ["STEPFUNCTIONS"],
401
+ CLOUDWATCHLOGS: ["LOGS"],
402
+ COGNITOIDENTITYPROVIDER: ["COGNITO"],
403
+ COGNITOIDENTITY: ["COGNITO"],
404
+ FIREHOSE: ["KINESISFIREHOSE"],
405
+ OPENSEARCH: ["OPENSEARCHSERVICE"],
406
+ ELASTICSEARCHSERVICE: ["ELASTICSEARCH"]
407
+ };
408
+ function resourceTypeMatchesEmulatedService(resourceType, services) {
409
+ const ns = /^AWS::([A-Za-z0-9]+)::/.exec(resourceType)?.[1]?.toUpperCase();
410
+ if (!ns) return false;
411
+ return services.some((s) => {
412
+ const id = s.replace(/_/g, "").toUpperCase();
413
+ return id === ns || (SERVICE_NAMESPACE_ALIASES[id] ?? []).includes(ns);
414
+ });
415
+ }
416
+ function createMixedModeStateProvider(opts) {
417
+ const { real, emulator, emulatedServices } = opts;
418
+ const swaps = /* @__PURE__ */ new Map();
419
+ const warned = /* @__PURE__ */ new Set();
420
+ const swap = (v) => swaps.get(v) ?? v;
421
+ const provider = {
422
+ label: real.label,
423
+ async load(stackName, synthRegion) {
424
+ const [r, e] = await Promise.all([real.load(stackName, synthRegion), emulator.load(stackName, synthRegion)]);
425
+ if (!e) {
426
+ if (r && !warned.has(stackName)) {
427
+ if (Object.values(r.resources).some((res) => resourceTypeMatchesEmulatedService(res.resourceType, emulatedServices))) {
428
+ warned.add(stackName);
429
+ const why = emulator.getLastLoadError?.();
430
+ getLogger().warn(`${real.label}: stack ${displayUntrustedValue(stackName)} could not be read from AWS Emulator${why ? ` (${why})` : ""}; resources of the emulated services (${emulatedServices.join(", ")}) keep their real AWS names. Override them with --env-vars, or deploy the stack into the emulator.`);
431
+ }
432
+ }
433
+ return r;
434
+ }
435
+ const emulated = (res) => resourceTypeMatchesEmulatedService(res.resourceType, emulatedServices);
436
+ if (!r) {
437
+ const resources = {};
438
+ for (const [id, res] of Object.entries(e.resources)) if (emulated(res)) resources[id] = res;
439
+ return {
440
+ resources,
441
+ outputs: { ...e.outputs },
442
+ region: e.region
443
+ };
444
+ }
445
+ const resources = { ...r.resources };
446
+ for (const [id, res] of Object.entries(e.resources)) {
447
+ if (!emulated(res)) continue;
448
+ const realRes = r.resources[id];
449
+ if (realRes?.physicalId && res.physicalId && realRes.physicalId !== res.physicalId) swaps.set(realRes.physicalId, res.physicalId);
450
+ resources[id] = res;
451
+ }
452
+ const outputs = {};
453
+ for (const [k, v] of Object.entries(e.outputs)) outputs[k] = v;
454
+ for (const [k, v] of Object.entries(r.outputs)) outputs[k] = swap(v);
455
+ return {
456
+ resources,
457
+ outputs,
458
+ region: r.region
459
+ };
460
+ },
461
+ buildCrossStackResolver: (region) => real.buildCrossStackResolver(region),
462
+ dispose() {
463
+ try {
464
+ real.dispose();
465
+ } finally {
466
+ emulator.dispose();
467
+ }
468
+ }
469
+ };
470
+ if (real.resolveDeployedFunctionEnv) {
471
+ const fn = real.resolveDeployedFunctionEnv.bind(real);
472
+ provider.resolveDeployedFunctionEnv = async (id) => {
473
+ const env = await fn(id);
474
+ if (!env) return env;
475
+ const out = {};
476
+ for (const [k, v] of Object.entries(env)) out[k] = swap(v);
477
+ return out;
478
+ };
479
+ }
480
+ if (real.resolveLambdaExecutionRoleArn) provider.resolveLambdaExecutionRoleArn = real.resolveLambdaExecutionRoleArn.bind(real);
481
+ if (real.resolveAgentCoreRuntimeRoleArn) provider.resolveAgentCoreRuntimeRoleArn = real.resolveAgentCoreRuntimeRoleArn.bind(real);
482
+ if (real.resolveTemplateSsmParameters) provider.resolveTemplateSsmParameters = real.resolveTemplateSsmParameters.bind(real);
483
+ if (real.getLastLoadError) provider.getLastLoadError = real.getLastLoadError.bind(real);
484
+ return provider;
485
+ }
486
+
487
+ //#endregion
488
+ //#region src/local/mixed-mode-tls.ts
489
+ function der(tag, content) {
490
+ const n = content.length;
491
+ let len;
492
+ if (n < 128) len = Buffer.from([n]);
493
+ else {
494
+ const bytes = [];
495
+ for (let v = n; v > 0; v = Math.floor(v / 256)) bytes.unshift(v & 255);
496
+ len = Buffer.from([128 | bytes.length, ...bytes]);
497
+ }
498
+ return Buffer.concat([
499
+ Buffer.from([tag]),
500
+ len,
501
+ content
502
+ ]);
503
+ }
504
+ const seq = (...parts) => der(48, Buffer.concat(parts));
505
+ const set = (...parts) => der(49, Buffer.concat(parts));
506
+ const octets = (b) => der(4, b);
507
+ const bits = (b, unused = 0) => der(3, Buffer.concat([Buffer.from([unused]), b]));
508
+ const bool = (v) => der(1, Buffer.from([v ? 255 : 0]));
509
+ const utf8 = (s) => der(12, Buffer.from(s, "utf8"));
510
+ const explicit = (n, inner) => der(160 + n, inner);
511
+ function integer(b) {
512
+ let v = b;
513
+ while (v.length > 1 && v[0] === 0 && (v[1] & 128) === 0) v = v.subarray(1);
514
+ if ((v[0] & 128) !== 0) v = Buffer.concat([Buffer.from([0]), v]);
515
+ return der(2, v);
516
+ }
517
+ function oid(dotted) {
518
+ const parts = dotted.split(".").map(Number);
519
+ const out = [40 * parts[0] + parts[1]];
520
+ for (const p of parts.slice(2)) {
521
+ const stack = [p & 127];
522
+ for (let v = Math.floor(p / 128); v > 0; v = Math.floor(v / 128)) stack.unshift(v & 127 | 128);
523
+ out.push(...stack);
524
+ }
525
+ return der(6, Buffer.from(out));
526
+ }
527
+ function utcTime(d) {
528
+ const p = (n) => String(n).padStart(2, "0");
529
+ const s = p(d.getUTCFullYear() % 100) + p(d.getUTCMonth() + 1) + p(d.getUTCDate()) + p(d.getUTCHours()) + p(d.getUTCMinutes()) + p(d.getUTCSeconds()) + "Z";
530
+ return der(23, Buffer.from(s, "ascii"));
531
+ }
532
+ const OID_ECDSA_SHA256 = "1.2.840.10045.4.3.2";
533
+ const OID_CN = "2.5.4.3";
534
+ const OID_SKI = "2.5.29.14";
535
+ const OID_KEY_USAGE = "2.5.29.15";
536
+ const OID_SAN = "2.5.29.17";
537
+ const OID_BASIC_CONSTRAINTS = "2.5.29.19";
538
+ const OID_AKI = "2.5.29.35";
539
+ const OID_EKU = "2.5.29.37";
540
+ const OID_NAME_CONSTRAINTS = "2.5.29.30";
541
+ const OID_SERVER_AUTH = "1.3.6.1.5.5.7.3.1";
542
+ const name = (cn) => seq(set(seq(oid(OID_CN), utf8(cn))));
543
+ function extension(id, critical, value) {
544
+ return seq(oid(id), ...critical ? [bool(true)] : [], octets(value));
545
+ }
546
+ function keyIdentifier(spkiDer) {
547
+ const point = spkiDer.subarray(spkiDer.length - 65);
548
+ return createHash("sha1").update(point).digest();
549
+ }
550
+ function serialNumber() {
551
+ const b = randomBytes(16);
552
+ b[0] = b[0] & 127 | 1;
553
+ return b;
554
+ }
555
+ function issue(spec) {
556
+ const sigAlg = seq(oid(OID_ECDSA_SHA256));
557
+ const spki = spec.publicKey.export({
558
+ type: "spki",
559
+ format: "der"
560
+ });
561
+ const tbs = seq(explicit(0, integer(Buffer.from([2]))), integer(serialNumber()), sigAlg, name(spec.issuer), seq(utcTime(spec.notBefore), utcTime(spec.notAfter)), name(spec.subject), spki, explicit(3, seq(...spec.extensions)));
562
+ const signature = sign("sha256", tbs, spec.signingKey);
563
+ return `-----BEGIN CERTIFICATE-----\n${seq(tbs, sigAlg, bits(signature)).toString("base64").replace(/(.{64})/g, "$1\n").replace(/\n$/, "")}\n-----END CERTIFICATE-----\n`;
564
+ }
565
+ function createMixedModeTlsMaterials(now = /* @__PURE__ */ new Date(), leafDnsNames = ["localhost"]) {
566
+ const notBefore = /* @__PURE__ */ new Date(now.getTime() - 3600 * 1e3);
567
+ const notAfter = new Date(now.getTime() + 720 * 60 * 60 * 1e3);
568
+ const ca = generateKeyPairSync("ec", { namedCurve: "prime256v1" });
569
+ const leaf = generateKeyPairSync("ec", { namedCurve: "prime256v1" });
570
+ const caKeyId = keyIdentifier(ca.publicKey.export({
571
+ type: "spki",
572
+ format: "der"
573
+ }));
574
+ const leafKeyId = keyIdentifier(leaf.publicKey.export({
575
+ type: "spki",
576
+ format: "der"
577
+ }));
578
+ const aki = (id) => seq(der(128, id));
579
+ const caCn = `cdk-local mixed-mode CA ${randomBytes(4).toString("hex")}`;
580
+ const caCertPem = issue({
581
+ subject: caCn,
582
+ issuer: caCn,
583
+ publicKey: ca.publicKey,
584
+ signingKey: ca.privateKey,
585
+ notBefore,
586
+ notAfter,
587
+ extensions: [
588
+ extension(OID_BASIC_CONSTRAINTS, true, seq(bool(true), integer(Buffer.from([0])))),
589
+ extension(OID_KEY_USAGE, true, bits(Buffer.from([6]), 1)),
590
+ extension(OID_SKI, false, octets(caKeyId)),
591
+ extension(OID_AKI, false, aki(caKeyId)),
592
+ extension(OID_NAME_CONSTRAINTS, true, seq(der(160, Buffer.concat([
593
+ seq(der(135, Buffer.from([
594
+ 127,
595
+ 0,
596
+ 0,
597
+ 1,
598
+ 255,
599
+ 255,
600
+ 255,
601
+ 255
602
+ ]))),
603
+ seq(der(135, Buffer.from([
604
+ ...new Array(15).fill(0),
605
+ 1,
606
+ ...new Array(16).fill(255)
607
+ ]))),
608
+ seq(der(130, Buffer.from("localhost", "ascii")))
609
+ ]))))
610
+ ]
611
+ });
612
+ const san = seq(der(135, Buffer.from([
613
+ 127,
614
+ 0,
615
+ 0,
616
+ 1
617
+ ])), der(135, Buffer.from([
618
+ 0,
619
+ 0,
620
+ 0,
621
+ 0,
622
+ 0,
623
+ 0,
624
+ 0,
625
+ 0,
626
+ 0,
627
+ 0,
628
+ 0,
629
+ 0,
630
+ 0,
631
+ 0,
632
+ 0,
633
+ 1
634
+ ])), ...leafDnsNames.map((n) => der(130, Buffer.from(n, "ascii"))));
635
+ return {
636
+ caCertPem,
637
+ certPem: issue({
638
+ subject: "127.0.0.1",
639
+ issuer: caCn,
640
+ publicKey: leaf.publicKey,
641
+ signingKey: ca.privateKey,
642
+ notBefore,
643
+ notAfter,
644
+ extensions: [
645
+ extension(OID_BASIC_CONSTRAINTS, true, seq()),
646
+ extension(OID_KEY_USAGE, true, bits(Buffer.from([128]), 7)),
647
+ extension(OID_EKU, false, seq(oid(OID_SERVER_AUTH))),
648
+ extension(OID_SAN, false, san),
649
+ extension(OID_SKI, false, octets(leafKeyId)),
650
+ extension(OID_AKI, false, aki(caKeyId))
651
+ ]
652
+ }),
653
+ keyPem: leaf.privateKey.export({
654
+ type: "pkcs8",
655
+ format: "pem"
656
+ }).toString()
657
+ };
658
+ }
659
+
660
+ //#endregion
661
+ //#region src/local/emulator-resign-sidecar.ts
662
+ const MIXED_MODE_RESIGN_BODY_CAP_BYTES = 100 * 1024 * 1024;
663
+ const RESIGN_SIDECAR_CORE = String.raw`
664
+ var crypto=require('crypto');
665
+ function sha256hex(b){return crypto.createHash('sha256').update(b).digest('hex');}
666
+ function hmac(k,s){return crypto.createHmac('sha256',k).update(s,'utf8').digest();}
667
+ function uriEncode(s){return encodeURIComponent(s).replace(/[!'()*]/g,function(c){return '%'+c.charCodeAt(0).toString(16).toUpperCase();});}
668
+ function safeDecode(s){try{return decodeURIComponent(s);}catch(e){return s;}}
669
+ function canonicalPath(rawPath,service){
670
+ var p=rawPath||'/';
671
+ if(service==='s3')return p;
672
+ var segs=[];
673
+ p.split('/').forEach(function(s){if(s===''||s==='.')return;if(s==='..')segs.pop();else segs.push(s);});
674
+ var n=(p.charAt(0)==='/'?'/':'')+segs.join('/')+(segs.length>0&&p.charAt(p.length-1)==='/'?'/':'');
675
+ return uriEncode(n).replace(/%2F/g,'/');
676
+ }
677
+ function splitQuery(q){
678
+ if(!q)return [];
679
+ return q.split('&').filter(function(x){return x!=='';}).map(function(part){
680
+ var i=part.indexOf('=');
681
+ return {raw:part,k:safeDecode(i<0?part:part.slice(0,i)),v:i<0?'':safeDecode(part.slice(i+1))};
682
+ });
683
+ }
684
+ function canonicalQuery(pairs){
685
+ return pairs.map(function(p){return [uriEncode(p.k),uriEncode(p.v)];})
686
+ .sort(function(a,b){return a[0]<b[0]?-1:a[0]>b[0]?1:a[1]<b[1]?-1:a[1]>b[1]?1:0;})
687
+ .map(function(e){return e[0]+'='+e[1];}).join('&');
688
+ }
689
+ var PRESIGN_PARAMS=new Set(['x-amz-algorithm','x-amz-credential','x-amz-date','x-amz-expires','x-amz-signedheaders','x-amz-signature','x-amz-security-token']);
690
+ function parseCredentialScope(algo,credential){
691
+ var parts=String(credential).split('/');
692
+ if(algo==='AWS4-HMAC-SHA256'&&parts.length>=5)return {region:parts[parts.length-3],service:parts[parts.length-2]};
693
+ if(algo==='AWS4-ECDSA-P256-SHA256'&&parts.length>=4)return {service:parts[parts.length-2]};
694
+ return undefined;
695
+ }
696
+ function parseAuthScope(authorization){
697
+ var m=/^\s*(AWS4-HMAC-SHA256|AWS4-ECDSA-P256-SHA256)\s+(.*)$/.exec(String(authorization));
698
+ if(!m)return undefined;
699
+ var c=/Credential=([^,\s]+)/.exec(m[2]);
700
+ return c?parseCredentialScope(m[1],c[1]):undefined;
701
+ }
702
+ var DROP_HEADERS=new Set(['authorization','host','connection','keep-alive','proxy-authorization','proxy-connection','te','trailer','transfer-encoding','upgrade','expect','content-length','x-amz-date','x-amz-security-token','x-amz-content-sha256','x-amz-decoded-content-length','x-amz-trailer']);
703
+ function upstreamHeaders(incoming){
704
+ var out=Object.create(null);
705
+ Object.keys(incoming).forEach(function(k){var lk=k.toLowerCase();if(DROP_HEADERS.has(lk))return;var v=incoming[k];if(v===undefined)return;out[lk]=Array.isArray(v)?v.join(', '):String(v);});
706
+ return out;
707
+ }
708
+ function isAwsChunked(headers){
709
+ var sha=String(headers['x-amz-content-sha256']||'');
710
+ return sha.indexOf('STREAMING-')===0||/(^|,)\s*aws-chunked\s*(,|$)/i.test(String(headers['content-encoding']||''));
711
+ }
712
+ function stripAwsChunked(ce){
713
+ if(!ce)return undefined;
714
+ var rest=String(ce).split(',').map(function(s){return s.trim();}).filter(function(s){return s!==''&&s.toLowerCase()!=='aws-chunked';});
715
+ return rest.length?rest.join(', '):undefined;
716
+ }
717
+ function decodeAwsChunked(buf){
718
+ var pos=0,parts=[],trailers=Object.create(null);
719
+ for(;;){
720
+ var eol=buf.indexOf('\r\n',pos);
721
+ if(eol<0)throw new Error('aws-chunked body: missing chunk header');
722
+ var size=parseInt(buf.toString('latin1',pos,eol).split(';')[0].trim(),16);
723
+ if(!(size>=0))throw new Error('aws-chunked body: bad chunk size');
724
+ pos=eol+2;
725
+ if(size===0)break;
726
+ if(pos+size+2>buf.length)throw new Error('aws-chunked body: truncated chunk');
727
+ parts.push(buf.subarray(pos,pos+size));
728
+ pos+=size;
729
+ if(buf[pos]!==13||buf[pos+1]!==10)throw new Error('aws-chunked body: missing chunk terminator');
730
+ pos+=2;
731
+ }
732
+ while(pos<buf.length){
733
+ var e=buf.indexOf('\r\n',pos),end=e<0?buf.length:e;
734
+ var line=buf.toString('latin1',pos,end);
735
+ pos=end+2;
736
+ if(line==='')break;
737
+ var c=line.indexOf(':');
738
+ if(c<=0)continue;
739
+ var k=line.slice(0,c).trim().toLowerCase();
740
+ if(k==='x-amz-trailer-signature')continue;
741
+ var v=line.slice(c+1).trim();
742
+ if(!/^[!#$%&'*+.^_|~0-9a-z-]+$/.test(k)||/[^ -~]/.test(v))throw new Error('aws-chunked body: invalid trailer');
743
+ trailers[k]=v;
744
+ }
745
+ return {body:Buffer.concat(parts),trailers:trailers};
746
+ }
747
+ function tooLargeByHeaders(headers,cap){
748
+ var decoded=Number(headers['x-amz-decoded-content-length']);
749
+ if(decoded>cap)return decoded;
750
+ var raw=Number(headers['content-length']);
751
+ if(raw>rawCap(cap))return raw;
752
+ return 0;
753
+ }
754
+ function rawCap(cap){return cap+Math.max(1048576,Math.floor(cap/100));}
755
+ function signRequest(o){
756
+ var amzDate=o.now.toISOString().replace(/[:-]|\.\d{3}/g,''),date=amzDate.slice(0,8),h=o.headers;
757
+ h['x-amz-date']=amzDate;
758
+ h['x-amz-content-sha256']=sha256hex(o.body);
759
+ if(o.creds.sessionToken)h['x-amz-security-token']=o.creds.sessionToken;
760
+ var names=Object.keys(h).filter(function(n){return n==='host'||n==='content-type'||n==='content-md5'||n.indexOf('x-amz-')===0;}).sort();
761
+ var canonHeaders=names.map(function(n){return n+':'+String(h[n]).trim().replace(/\s+/g,' ')+'\n';}).join('');
762
+ var signed=names.join(';');
763
+ var creq=[o.method,canonicalPath(o.path,o.service),canonicalQuery(splitQuery(o.query)),canonHeaders,signed,h['x-amz-content-sha256']].join('\n');
764
+ var scope=date+'/'+o.region+'/'+o.service+'/aws4_request';
765
+ var sts=['AWS4-HMAC-SHA256',amzDate,scope,sha256hex(creq)].join('\n');
766
+ var k=hmac(hmac(hmac(hmac('AWS4'+o.creds.secretAccessKey,date),o.region),o.service),'aws4_request');
767
+ var sig=crypto.createHmac('sha256',k).update(sts,'utf8').digest('hex');
768
+ h['authorization']='AWS4-HMAC-SHA256 Credential='+o.creds.accessKeyId+'/'+scope+', SignedHeaders='+signed+', Signature='+sig;
769
+ return h;
770
+ }
771
+ function prepareResign(req,body,cfg,now){
772
+ var url=String(req.url||'/'),qi=url.indexOf('?');
773
+ var path=qi<0?url:url.slice(0,qi),query=qi<0?'':url.slice(qi+1);
774
+ var incoming=req.headers,scope,signedRequest=false;
775
+ var pairs=splitQuery(query),cred,algo;
776
+ pairs.forEach(function(p){var lk=p.k.toLowerCase();if(lk==='x-amz-credential')cred=p.v;if(lk==='x-amz-algorithm')algo=p.v;});
777
+ // Presign parameters (a real token or signature among them) never go upstream,
778
+ // whether or not the request ALSO carries a header signature.
779
+ query=pairs.filter(function(p){return !PRESIGN_PARAMS.has(p.k.toLowerCase());}).map(function(p){return p.raw;}).join('&');
780
+ if(incoming['authorization']!==undefined){
781
+ signedRequest=true;
782
+ scope=parseAuthScope(incoming['authorization']);
783
+ }else if(cred!==undefined){
784
+ signedRequest=true;
785
+ scope=parseCredentialScope(algo||'AWS4-HMAC-SHA256',cred);
786
+ }
787
+ if(signedRequest&&!scope)return {error:{status:400,code:'IncompleteSignature',message:'cdk-local mixed mode: the request signature has no credential scope the AWS Emulator re-signing sidecar can read.'}};
788
+ var service=scope?scope.service:undefined;
789
+ var headers=upstreamHeaders(incoming);
790
+ if(isAwsChunked(incoming)){
791
+ var decoded;
792
+ try{decoded=decodeAwsChunked(body);}catch(e){return {error:{status:400,code:'IncompleteBody',message:'cdk-local mixed mode: '+e.message+'.'}};}
793
+ body=decoded.body;
794
+ Object.keys(decoded.trailers).forEach(function(k){if(!DROP_HEADERS.has(k))headers[k]=decoded.trailers[k];});
795
+ var ce=stripAwsChunked(headers['content-encoding']);
796
+ if(ce===undefined)delete headers['content-encoding'];else headers['content-encoding']=ce;
797
+ }
798
+ if(body.length>cfg.capBytes)return {error:tooLargeError(body.length,cfg.capBytes,service)};
799
+ headers['host']=cfg.upstreamHost;
800
+ headers['content-length']=String(body.length);
801
+ if(signedRequest){
802
+ var regionSet=incoming['x-amz-region-set'];
803
+ pairs.forEach(function(p){if(p.k.toLowerCase()==='x-amz-region-set'&&regionSet===undefined)regionSet=p.v;});
804
+ var region=scope.region||String(regionSet||'').split(',')[0].trim()||cfg.region||'us-east-1';
805
+ if(region==='*')region=cfg.region||'us-east-1';
806
+ signRequest({method:String(req.method||'GET'),path:path,query:query,headers:headers,body:body,region:region,service:service,creds:cfg.credentials,now:now});
807
+ }
808
+ return {path:query?path+'?'+query:path,headers:headers,body:body,service:service};
809
+ }
810
+ function tooLargeError(size,cap,service){
811
+ return {status:413,code:service==='s3'?'EntityTooLarge':'RequestEntityTooLargeException',message:'cdk-local mixed mode: the request body ('+size+' bytes) is over the '+cap+'-byte cap of the AWS Emulator re-signing sidecar, which holds a body in memory to sign it. Upload a large S3 object with multipart upload (each part under the cap), e.g. boto3 upload_file or @aws-sdk/lib-storage.'};
812
+ }
813
+ function errorBody(err,service){
814
+ if(service==='s3'||service===undefined)return {type:'application/xml',text:'<?xml version="1.0" encoding="UTF-8"?><Error><Code>'+err.code+'</Code><Message>'+err.message.replace(/&/g,'&amp;').replace(/</g,'&lt;')+'</Message></Error>'};
815
+ return {type:'application/x-amz-json-1.0',text:JSON.stringify({__type:err.code,message:err.message})};
816
+ }
817
+ `;
818
+ const RESIGN_SIDECAR_SERVER = String.raw`
819
+ function startResigner(cfgPath){return new Promise(function(done){
820
+ var fs=require('fs'),https=require('https'),http=require('http'),tls=require('tls');
821
+ var cfg;
822
+ try{cfg=JSON.parse(fs.readFileSync(cfgPath,'utf8'));}catch(e){done({error:'CONFIG_'+(e.code||'ERROR')});return;}
823
+ var up=new URL(cfg.upstream),secure=up.protocol==='https:',mod=secure?https:http;
824
+ cfg.upstreamHost=up.host;
825
+ var agentOpts={keepAlive:true};
826
+ if(secure&&cfg.upstreamCaPem)agentOpts.ca=tls.rootCertificates.concat([cfg.upstreamCaPem]);
827
+ var agent=new mod.Agent(agentOpts);
828
+ function send(resp,status,err,service){
829
+ if(resp.headersSent){resp.destroy();return;}
830
+ var b=errorBody(err,service);
831
+ resp.writeHead(status,{'content-type':b.type,'content-length':Buffer.byteLength(b.text),'connection':'close'});
832
+ resp.end(b.text);
833
+ }
834
+ function serviceOf(req){
835
+ var s=parseAuthScope(req.headers['authorization']||'');
836
+ if(!s){var m=/[?&]X-Amz-Credential=([^&]*)/i.exec(String(req.url||''));if(m)s=parseCredentialScope('AWS4-HMAC-SHA256',safeDecode(m[1]));}
837
+ return s?s.service:undefined;
838
+ }
839
+ // Bodies held at once, across concurrent requests: twice the per-request cap.
840
+ // Each held byte is charged twice: building the request body briefly keeps
841
+ // two copies alive (the received chunks while they are joined, then the
842
+ // joined body while an aws-chunked body is decoded), so the charged total
843
+ // bounds the real peak by inflightCap.
844
+ var inflight=0,inflightCap=2*rawCap(cfg.capBytes);
845
+ function handle(req,resp){
846
+ // held counts toward inflight until the response is over; seen is the
847
+ // body size for the 413 message.
848
+ var chunks=[],held=0,seen=0,over=false,busy=false,limit=rawCap(cfg.capBytes);
849
+ var drop=function(){inflight-=2*held;held=0;chunks=[];};
850
+ req.on('data',function(c){seen+=c.length;if(over||busy)return;if(seen>limit){over=true;drop();return;}if(inflight+2*c.length>inflightCap){busy=true;drop();return;}held+=c.length;inflight+=2*c.length;chunks.push(c);});
851
+ req.on('error',function(){drop();resp.destroy();});
852
+ resp.on('close',drop);
853
+ req.on('end',function(){
854
+ if(busy){send(resp,503,{code:'SlowDown',message:'cdk-local mixed mode: the AWS Emulator re-signing sidecar is holding too many request bodies at once; retry.'},serviceOf(req));return;}
855
+ if(over){process.stdout.write('CDKL-RESIGN rejected a streamed request body over the cap without holding it\n');send(resp,413,tooLargeError(seen,cfg.capBytes,serviceOf(req)),serviceOf(req));return;}
856
+ var p;
857
+ var joined=Buffer.concat(chunks);chunks=[];
858
+ try{p=prepareResign(req,joined,cfg,new Date());}catch(e){p={error:{status:400,code:'InvalidRequest',message:'cdk-local mixed mode: the request could not be re-signed.'}};}
859
+ joined=null;
860
+ if(p.error){if(p.error.status===413)process.stdout.write('CDKL-RESIGN rejected a request body over the cap\n');send(resp,p.error.status,p.error,serviceOf(req));return;}
861
+ var r;
862
+ try{r=mod.request({host:up.hostname.replace(/^\[|\]$/g,''),port:up.port||(secure?443:80),method:req.method,path:p.path,headers:p.headers,agent:agent},function(ur){
863
+ var h={};
864
+ Object.keys(ur.headers).forEach(function(k){if(k==='connection'||k==='keep-alive'||k==='transfer-encoding')return;h[k]=ur.headers[k];});
865
+ resp.writeHead(ur.statusCode||502,h);
866
+ ur.pipe(resp);
867
+ ur.on('error',function(){resp.destroy();});
868
+ });}catch(e){send(resp,400,{code:'InvalidRequest',message:'cdk-local mixed mode: the request has a header the AWS Emulator re-signing sidecar cannot forward.'},p.service);return;}
869
+ // A client that hangs up (an aborted GetObject) frees the upstream socket too.
870
+ resp.on('close',function(){if(!resp.writableFinished)r.destroy();});
871
+ r.setTimeout(300000,function(){r.destroy(Object.assign(new Error('upstream timeout'),{code:'ETIMEDOUT'}));});
872
+ r.on('error',function(e){process.stdout.write('CDKL-RESIGN upstream error '+(e.code||'ERROR')+'\n');send(resp,502,{code:'BadGateway',message:'cdk-local mixed mode: the AWS Emulator at '+cfg.upstream+' did not answer ('+(e.code||'ERROR')+').'},p.service);});
873
+ r.end(p.body);
874
+ });
875
+ }
876
+ var srv=https.createServer({key:cfg.keyPem,cert:cfg.certPem},handle);
877
+ srv.on('checkContinue',function(req,resp){
878
+ var n=tooLargeByHeaders(req.headers,cfg.capBytes);
879
+ if(n>0){process.stdout.write('CDKL-RESIGN rejected a request body over the cap\n');send(resp,413,tooLargeError(n,cfg.capBytes,serviceOf(req)),serviceOf(req));req.resume();return;}
880
+ resp.writeContinue();
881
+ handle(req,resp);
882
+ });
883
+ srv.once('error',function(e){done({error:e.code||'ERROR',port:cfg.port});});
884
+ // Loopback only: it signs with the emulator's keys for whoever connects.
885
+ srv.listen(cfg.port,'127.0.0.1',function(){done({port:cfg.port,address:srv.address().address});});
886
+ });}
887
+ `;
888
+ const RESIGN_SIDECAR_SCRIPT = RESIGN_SIDECAR_CORE + RESIGN_SIDECAR_SERVER;
889
+
349
890
  //#endregion
350
891
  //#region src/local/workload-aws-wiring.ts
351
892
  var workload_aws_wiring_exports = /* @__PURE__ */ __exportAll({
@@ -355,6 +896,7 @@ var workload_aws_wiring_exports = /* @__PURE__ */ __exportAll({
355
896
  EMULATOR_SCRUBBED_ENV_KEYS: () => EMULATOR_SCRUBBED_ENV_KEYS,
356
897
  HOST_CA_TRUST_ENV_KEYS: () => HOST_CA_TRUST_ENV_KEYS,
357
898
  HOST_DEFAULT_CREDENTIALS_MISS_TTL_MS: () => HOST_DEFAULT_CREDENTIALS_MISS_TTL_MS,
899
+ STALE_RESIGN_DIR_AGE_MS: () => STALE_RESIGN_DIR_AGE_MS,
358
900
  activateWorkloadAwsWiring: () => activateWorkloadAwsWiring,
359
901
  applyWorkloadAwsWiringToEnv: () => applyWorkloadAwsWiringToEnv,
360
902
  awsEmulatorEnvName: () => awsEmulatorEnvName,
@@ -365,6 +907,7 @@ var workload_aws_wiring_exports = /* @__PURE__ */ __exportAll({
365
907
  getContainerCaBundlePath: () => getContainerCaBundlePath,
366
908
  getContainerJavaTruststorePath: () => getContainerJavaTruststorePath,
367
909
  javaTruststoreToolOptions: () => javaTruststoreToolOptions,
910
+ loopbackForwarderPlan: () => loopbackForwarderPlan,
368
911
  mergeJavaToolOptions: () => mergeJavaToolOptions,
369
912
  parseAwsEndpoint: () => parseAwsEndpoint,
370
913
  parseIniSections: () => parseIniSections,
@@ -375,6 +918,7 @@ var workload_aws_wiring_exports = /* @__PURE__ */ __exportAll({
375
918
  resolveHostDefaultCredentials: () => resolveHostDefaultCredentials,
376
919
  resolveWorkloadAwsWiring: () => resolveWorkloadAwsWiring,
377
920
  setActiveWorkloadAwsWiring: () => setActiveWorkloadAwsWiring,
921
+ sweepStaleResignDirs: () => sweepStaleResignDirs,
378
922
  writeCaBundleFile: () => writeCaBundleFile
379
923
  });
380
924
  const CA_TRUST_ENV_KEYS = [
@@ -559,15 +1103,20 @@ function getContainerCaBundlePath() {
559
1103
  function expandHome(p, homeDir) {
560
1104
  return p === "~" ? homeDir : p.startsWith("~/") ? path$1.join(homeDir, p.slice(2)) : p;
561
1105
  }
562
- function writeCaBundleFile(source) {
563
- let pem;
564
- try {
565
- pem = readFileSync(source, "utf8");
566
- } catch (err) {
567
- throw new Error(`CA bundle ${displayUntrustedValue(source)} cannot be read (${err.code ?? "error"}).`);
1106
+ function writeCaBundleFile(source, extraCertsPem = []) {
1107
+ const certs = [];
1108
+ if (source !== void 0) {
1109
+ let pem;
1110
+ try {
1111
+ pem = readFileSync(source, "utf8");
1112
+ } catch (err) {
1113
+ throw new Error(`CA bundle ${displayUntrustedValue(source)} cannot be read (${err.code ?? "error"}).`);
1114
+ }
1115
+ const found = pemCertificates(pem);
1116
+ if (found.length === 0) throw new Error(`CA bundle ${displayUntrustedValue(source)} contains no PEM certificate.`);
1117
+ certs.push(...found);
568
1118
  }
569
- const certs = pem.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g);
570
- if (!certs) throw new Error(`CA bundle ${displayUntrustedValue(source)} contains no PEM certificate.`);
1119
+ for (const extra of extraCertsPem) certs.push(...pemCertificates(extra));
571
1120
  const dir = mkdtempSync(path$1.join(tmpdir(), `${getEmbedConfig().productName}-ca-`));
572
1121
  const hostPath = path$1.join(dir, "ca-bundle.pem");
573
1122
  writeFileSync(hostPath, `${certs.join("\n")}\n${rootCertificates.join("\n")}\n`, { mode: 420 });
@@ -579,6 +1128,9 @@ function writeCaBundleFile(source) {
579
1128
  })
580
1129
  };
581
1130
  }
1131
+ function pemCertificates(pem) {
1132
+ return pem.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g) ?? [];
1133
+ }
582
1134
  const DEFAULT_AWS_EMULATOR_URL = "https://127.0.0.1:9297";
583
1135
  function probeAwsEmulatorHealth(url, caPath) {
584
1136
  return new Promise((resolve) => {
@@ -607,6 +1159,108 @@ function probeAwsEmulatorHealth(url, caPath) {
607
1159
  function describeEmulatorUnreachable(ep, reason) {
608
1160
  return `--aws-emulator ${ep.url}: no AWS Emulator answers its health check at ${ep.url}/__admin/health (${reason}). Check that it is running on port ${ep.port} (\`aws-emulator -p ${ep.port}\`, or its Docker image published with \`-p 127.0.0.1:${ep.port}:${ep.port}\`), that the URL's port matches, and that it listens on an address this machine reaches (\`-b\`).`;
609
1161
  }
1162
+ const STALE_RESIGN_DIR_AGE_MS = 10800 * 1e3;
1163
+ function sweepStaleResignDirs(now = Date.now(), dir = tmpdir()) {
1164
+ const removed = [];
1165
+ const prefix = `${getEmbedConfig().productName}-resign-`;
1166
+ const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
1167
+ let names;
1168
+ try {
1169
+ names = readdirSync(dir);
1170
+ } catch {
1171
+ return removed;
1172
+ }
1173
+ for (const name of names) {
1174
+ if (!name.startsWith(prefix)) continue;
1175
+ const full = path$1.join(dir, name);
1176
+ try {
1177
+ const st = lstatSync(full);
1178
+ if (!st.isDirectory() || uid !== void 0 && st.uid !== uid) continue;
1179
+ if (now - st.mtimeMs < 108e5) continue;
1180
+ const pid = Number(readFileSync(path$1.join(full, "owner.pid"), "utf8").trim());
1181
+ if (!Number.isInteger(pid) || pid <= 0 || processAlive(pid)) continue;
1182
+ rmSync(full, {
1183
+ recursive: true,
1184
+ force: true
1185
+ });
1186
+ removed.push(full);
1187
+ } catch {}
1188
+ }
1189
+ return removed;
1190
+ }
1191
+ function processAlive(pid) {
1192
+ try {
1193
+ process.kill(pid, 0);
1194
+ return true;
1195
+ } catch (err) {
1196
+ return err.code === "EPERM";
1197
+ }
1198
+ }
1199
+ function resolveMixedModeWiring(args) {
1200
+ const { ep, services, credentials, caSources, disposers } = args;
1201
+ const caPems = [];
1202
+ for (const source of caSources) try {
1203
+ caPems.push(...pemCertificates(readFileSync(source, "utf8")));
1204
+ } catch (err) {
1205
+ throw new Error(`--aws-emulator: the CA bundle ${displayUntrustedValue(source)} cannot be read (${err.code ?? "error"}).`);
1206
+ }
1207
+ const upstreamCaPem = caPems.length > 0 ? caPems.join("\n") : void 0;
1208
+ const tls = createMixedModeTlsMaterials();
1209
+ const bundle = writeCaBundleFile(void 0, [...caPems, tls.caCertPem]);
1210
+ disposers.push(bundle.dispose);
1211
+ sweepStaleResignDirs();
1212
+ const dir = mkdtempSync(path$1.join(tmpdir(), `${getEmbedConfig().productName}-resign-`));
1213
+ disposers.push(() => rmSync(dir, {
1214
+ recursive: true,
1215
+ force: true
1216
+ }));
1217
+ writeFileSync(path$1.join(dir, "owner.pid"), String(process.pid), { mode: 384 });
1218
+ const resignConfigHostPath = path$1.join(dir, "resign.json");
1219
+ const config = {
1220
+ port: MIXED_MODE_RESIGN_PORT,
1221
+ upstream: ep.url,
1222
+ ...upstreamCaPem ? { upstreamCaPem } : {},
1223
+ certPem: tls.certPem,
1224
+ keyPem: tls.keyPem,
1225
+ credentials: {
1226
+ accessKeyId: credentials.accessKeyId,
1227
+ secretAccessKey: credentials.secretAccessKey,
1228
+ ...credentials.sessionToken ? { sessionToken: credentials.sessionToken } : {}
1229
+ },
1230
+ capBytes: MIXED_MODE_RESIGN_BODY_CAP_BYTES,
1231
+ ...args.region ? { region: args.region } : {}
1232
+ };
1233
+ writeFileSync(resignConfigHostPath, JSON.stringify(config), { mode: 384 });
1234
+ const containerPath = getContainerCaBundlePath();
1235
+ const trust = {};
1236
+ for (const k of CA_TRUST_ENV_KEYS) trust[k] = containerPath;
1237
+ return {
1238
+ mode: "forward",
1239
+ mixed: {
1240
+ services,
1241
+ emulatorEndpoint: ep,
1242
+ resignPort: MIXED_MODE_RESIGN_PORT,
1243
+ resignEndpoint: `https://127.0.0.1:${MIXED_MODE_RESIGN_PORT}`,
1244
+ resignConfigHostPath,
1245
+ emulatorClientOverride: {
1246
+ endpoint: ep.url,
1247
+ ...upstreamCaPem ? { caPem: upstreamCaPem } : {},
1248
+ credentials: config.credentials
1249
+ }
1250
+ },
1251
+ env: trust,
1252
+ scrubEnvKeys: [],
1253
+ mounts: [{
1254
+ hostPath: bundle.hostPath,
1255
+ containerPath
1256
+ }],
1257
+ loopbackPorts: ep.loopback ? [ep.port, MIXED_MODE_RESIGN_PORT] : [MIXED_MODE_RESIGN_PORT],
1258
+ probeHealth: true,
1259
+ caHostPath: bundle.hostPath,
1260
+ ...args.skipPull ? { skipPull: true } : {},
1261
+ dispose: args.dispose
1262
+ };
1263
+ }
610
1264
  async function resolveWorkloadAwsWiring(opts) {
611
1265
  const env = opts.env ?? process.env;
612
1266
  const homeDir = opts.homeDir ?? homedir();
@@ -640,14 +1294,30 @@ async function resolveWorkloadAwsWiring(opts) {
640
1294
  }
641
1295
  const run = readEmulatorRunFiles(ep.port, homeDir);
642
1296
  const caSource = opts.caBundle || run.caPath || env["AWS_CA_BUNDLE"];
643
- const failure = await (opts.probeHealth ?? probeAwsEmulatorHealth)(ep.url, caSource ? expandHome(caSource, homeDir) : void 0);
1297
+ const mixedServices = emulatedServicesOf(opts.awsEmulatorSentinels ?? []);
1298
+ const probeCa = mixedServices.length > 0 ? run.caPath || caSource : caSource;
1299
+ const failure = await (opts.probeHealth ?? probeAwsEmulatorHealth)(ep.url, probeCa ? expandHome(probeCa, homeDir) : void 0);
644
1300
  if (failure) throw new Error(describeEmulatorUnreachable(ep, failure));
645
1301
  let credentials;
646
1302
  if (run.credentials) credentials = run.credentials;
647
1303
  else {
648
1304
  credentials = AUTH_OFF_PLACEHOLDER_CREDENTIALS;
649
- getLogger().info(`--aws-emulator: ${path$1.join(run.dir, "emulator_credentials")} not found but the emulator is healthy, so it runs with --auth off; workload containers get placeholder credentials.`);
1305
+ getLogger().info(`--aws-emulator: ${path$1.join(run.dir, "emulator_credentials")} not found but the emulator is healthy, ` + (mixedServices.length > 0 ? "so it runs with --auth off; the re-signing sidecar signs with placeholder credentials." : "so it runs with --auth off; workload containers get placeholder credentials."));
650
1306
  }
1307
+ if (mixedServices.length > 0) return resolveMixedModeWiring({
1308
+ ep,
1309
+ services: mixedServices,
1310
+ credentials,
1311
+ caSources: [...new Set([
1312
+ ep.url.startsWith("https:") ? run.caPath : void 0,
1313
+ opts.caBundle,
1314
+ env["AWS_CA_BUNDLE"]
1315
+ ].filter((c) => typeof c === "string" && c !== "").map((c) => expandHome(c, homeDir)))],
1316
+ disposers,
1317
+ dispose,
1318
+ skipPull: opts.skipPull,
1319
+ region: env["AWS_REGION"] || env["AWS_DEFAULT_REGION"] || void 0
1320
+ });
651
1321
  const ca = caSource && ep.url.startsWith("https:") ? caMount(caSource) : void 0;
652
1322
  let java;
653
1323
  if (run.truststorePath && ep.url.startsWith("https:")) {
@@ -736,8 +1406,18 @@ function effectiveLoopbackPorts(env, wiring) {
736
1406
  if (ep.loopback) used.add(ep.port);
737
1407
  } catch {}
738
1408
  }
1409
+ const mixed = wiring.mixed;
1410
+ if (mixed && used.has(mixed.resignPort) && mixed.emulatorEndpoint.loopback) used.add(mixed.emulatorEndpoint.port);
739
1411
  return wiring.loopbackPorts.filter((p) => used.has(p));
740
1412
  }
1413
+ function loopbackForwarderPlan(wiring, ports) {
1414
+ const mixed = wiring.mixed;
1415
+ if (!mixed || !ports.includes(mixed.resignPort)) return { ports: [...ports] };
1416
+ return {
1417
+ ports: ports.filter((p) => p !== mixed.resignPort),
1418
+ resignConfigHostPath: mixed.resignConfigHostPath
1419
+ };
1420
+ }
741
1421
  function applyWorkloadAwsWiringToEnv(env, wiring, defaultCredentials) {
742
1422
  const out = {};
743
1423
  for (const [k, v] of Object.entries(env)) Object.defineProperty(out, k, {
@@ -746,6 +1426,11 @@ function applyWorkloadAwsWiringToEnv(env, wiring, defaultCredentials) {
746
1426
  configurable: true,
747
1427
  writable: true
748
1428
  });
1429
+ for (const [k, v] of Object.entries(out)) {
1430
+ if (!isAwsEmulatorSentinel(k, v)) continue;
1431
+ if (!wiring.mixed || k === "AWS_ENDPOINT_URL") throw new Error(`${k}="${AWS_EMULATOR_SENTINEL}" reached a workload container outside mixed mode. The sentinel is read from the --env-vars file, on an AWS_ENDPOINT_URL_<SERVICE> variable, and needs --aws-emulator [url].`);
1432
+ out[k] = wiring.mixed.resignEndpoint;
1433
+ }
749
1434
  const setCreds = (c) => {
750
1435
  for (const k of CREDENTIAL_KEYS) delete out[k];
751
1436
  if (!c) return;
@@ -824,14 +1509,21 @@ function resolveAwsEmulatorOption(flag, env = process.env) {
824
1509
  return /^(1|true)$/i.test(raw) ? true : raw;
825
1510
  }
826
1511
  async function activateWorkloadAwsWiring(options) {
1512
+ const awsEmulator = resolveAwsEmulatorOption(options.awsEmulator);
1513
+ const sentinels = readAwsEmulatorSentinelsFromFile(options.envVars);
1514
+ validateAwsEmulatorSentinels(sentinels, awsEmulator !== void 0, awsEmulatorEnvName());
827
1515
  const wiring = await resolveWorkloadAwsWiring({
828
- awsEmulator: resolveAwsEmulatorOption(options.awsEmulator),
1516
+ awsEmulator,
829
1517
  caBundle: options.caBundle,
830
1518
  profile: options.profile,
831
- skipPull: options.pull === false
1519
+ skipPull: options.pull === false,
1520
+ awsEmulatorSentinels: sentinels
832
1521
  });
833
1522
  setActiveWorkloadAwsWiring(wiring);
834
- if (wiring.mode === "emulator" && wiring.emulatorEndpoint) {
1523
+ if (wiring.mixed) {
1524
+ setAwsEndpointOverride(void 0);
1525
+ getLogger().info(`Mixed mode: ${wiring.mixed.services.join(", ")} go to AWS Emulator at ${wiring.mixed.emulatorEndpoint.url} (re-signed with its keys); every other AWS call uses your real credentials.`);
1526
+ } else if (wiring.mode === "emulator" && wiring.emulatorEndpoint) {
835
1527
  setAwsEndpointOverride({
836
1528
  endpoint: wiring.emulatorEndpoint.url,
837
1529
  ...wiring.caHostPath ? { caPem: readFileSync(wiring.caHostPath, "utf8") } : {},
@@ -994,5 +1686,5 @@ function warnWorkloadCredentialsStripped() {
994
1686
  }
995
1687
 
996
1688
  //#endregion
997
- export { probeAwsEmulatorHealth as A, getAwsEndpointOverride as B, effectiveLoopbackPorts as C, javaTruststoreToolOptions as D, getContainerJavaTruststorePath as E, resolveWorkloadAwsWiring as F, isWildcardHostname as G, proxyAwareFetch as H, setActiveWorkloadAwsWiring as I, hostFromAuthority as J, formatAuthority as K, workload_aws_wiring_exports as L, readProfileEndpointSettings as M, resolveAwsEmulatorOption as N, mergeJavaToolOptions as O, resolveHostDefaultCredentials as P, writeCaBundleFile as R, displayEndpointValue as S, getContainerCaBundlePath as T, setAwsEndpointOverride as U, isProxyEnvConfigured as V, isLoopbackHostname as W, HOST_DEFAULT_CREDENTIALS_MISS_TTL_MS as _, deployAccountStsClientConfig as a, awsEmulatorEnvName as b, resetWorkloadIdentity as c, workloadStsClientConfig as d, AUTH_OFF_PLACEHOLDER_CREDENTIALS as f, HOST_CA_TRUST_ENV_KEYS as g, EMULATOR_SCRUBBED_ENV_KEYS as h, assumedRoleRecorded as i, readEmulatorRunFiles as j, parseAwsEndpoint as k, warnWorkloadCredentialsStripped as l, DEFAULT_AWS_EMULATOR_URL as m, accountLookupStsClientConfig as n, readEnvCallerCredentials as o, CA_TRUST_ENV_KEYS as p, formatHostForAuthority as q, applyWorkloadEnvCredentials as r, recordAssumedRole as s, SECOND_ASSUME_MESSAGE as t, workloadClientConfig as u, activateWorkloadAwsWiring as v, getActiveWorkloadAwsWiring as w, collectHostEndpointOverrides as x, applyWorkloadAwsWiringToEnv as y, buildProxyClientConfig as z };
998
- //# sourceMappingURL=workload-identity-ITM3p2Ed.js.map
1689
+ export { buildProxyClientConfig as $, parseAwsEndpoint as A, MIXED_MODE_RESIGN_BODY_CAP_BYTES as B, effectiveLoopbackPorts as C, javaTruststoreToolOptions as D, getContainerJavaTruststorePath as E, resolveHostDefaultCredentials as F, createMixedModeStateProvider as G, createMixedModeTlsMaterials as H, resolveWorkloadAwsWiring as I, isAwsEmulatorSentinel as J, emulatedServicesOf as K, setActiveWorkloadAwsWiring as L, readEmulatorRunFiles as M, readProfileEndpointSettings as N, loopbackForwarderPlan as O, resolveAwsEmulatorOption as P, awsEndpointOverrideClientConfig as Q, workload_aws_wiring_exports as R, displayEndpointValue as S, getContainerCaBundlePath as T, AWS_EMULATOR_SENTINEL as U, RESIGN_SIDECAR_SCRIPT as V, MIXED_MODE_RESIGN_PORT as W, resourceTypeMatchesEmulatedService as X, readAwsEmulatorSentinelsFromFile as Y, validateAwsEmulatorSentinels as Z, HOST_DEFAULT_CREDENTIALS_MISS_TTL_MS as _, deployAccountStsClientConfig as a, isWildcardHostname as at, awsEmulatorEnvName as b, resetWorkloadIdentity as c, hostFromAuthority as ct, workloadStsClientConfig as d, getAwsEndpointOverride as et, AUTH_OFF_PLACEHOLDER_CREDENTIALS as f, HOST_CA_TRUST_ENV_KEYS as g, EMULATOR_SCRUBBED_ENV_KEYS as h, assumedRoleRecorded as i, isLoopbackHostname as it, probeAwsEmulatorHealth as j, mergeJavaToolOptions as k, warnWorkloadCredentialsStripped as l, DEFAULT_AWS_EMULATOR_URL as m, accountLookupStsClientConfig as n, proxyAwareFetch as nt, readEnvCallerCredentials as o, formatAuthority as ot, CA_TRUST_ENV_KEYS as p, findAwsEmulatorSentinels as q, applyWorkloadEnvCredentials as r, setAwsEndpointOverride as rt, recordAssumedRole as s, formatHostForAuthority as st, SECOND_ASSUME_MESSAGE as t, isProxyEnvConfigured as tt, workloadClientConfig as u, activateWorkloadAwsWiring as v, getActiveWorkloadAwsWiring as w, collectHostEndpointOverrides as x, applyWorkloadAwsWiringToEnv as y, writeCaBundleFile as z };
1690
+ //# sourceMappingURL=workload-identity-D5zkhVcF.js.map