@awsless/cli 0.0.46-local.51 → 0.0.46-local.52

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.
Files changed (2) hide show
  1. package/dist/bin.js +648 -273
  2. package/package.json +12 -12
package/dist/bin.js CHANGED
@@ -103012,7 +103012,7 @@ import { fromEnv as fromEnv3 } from "@aws-sdk/credential-providers";
103012
103012
  import { Client as Client4 } from "@opensearch-project/opensearch";
103013
103013
  import { AwsSigv4Signer as AwsSigv4Signer2 } from "@opensearch-project/opensearch/aws";
103014
103014
  import { Agent } from "https";
103015
- import { createHash as createHash16 } from "crypto";
103015
+ import { createHash as createHash15 } from "crypto";
103016
103016
  import { mkdir as mkdir6, rename as rename4, rm as rm8, stat as stat5 } from "fs/promises";
103017
103017
  import { join as join27, resolve as resolve3 } from "path";
103018
103018
  import { spawn as spawn3 } from "child_process";
@@ -103084,7 +103084,7 @@ var import_decompress, import_sleep_await, mock, searchClient = (options = {}, s
103084
103084
  throw new Error(`Downloading the OpenSearch checksum failed with status ${checksumResponse.status}: ${url}.sha512`);
103085
103085
  }
103086
103086
  const checksum = (await checksumResponse.text()).split(/\s+/)[0];
103087
- const digest = createHash16("sha512").update(buffer).digest("hex");
103087
+ const digest = createHash15("sha512").update(buffer).digest("hex");
103088
103088
  if (digest !== checksum) {
103089
103089
  throw new Error(`The OpenSearch archive doesn't match its published sha512 checksum: ${url}`);
103090
103090
  }
@@ -103753,7 +103753,7 @@ var require_find_exec = __commonJS((exports, module) => {
103753
103753
  var require_play_sound = __commonJS((exports, module) => {
103754
103754
  var fs5 = __require("fs");
103755
103755
  var findExec = require_find_exec();
103756
- var spawn5 = __require("child_process").spawn;
103756
+ var spawn6 = __require("child_process").spawn;
103757
103757
  var players = [
103758
103758
  "mplayer",
103759
103759
  "afplay",
@@ -103783,7 +103783,7 @@ var require_play_sound = __commonJS((exports, module) => {
103783
103783
  return next(new Error("Couldn't find a suitable audio player"));
103784
103784
  }
103785
103785
  var args = Array.isArray(options[this.player]) ? options[this.player].concat(what) : [what];
103786
- var process5 = spawn5(this.player, args, options);
103786
+ var process5 = spawn6(this.player, args, options);
103787
103787
  if (!process5) {
103788
103788
  next(new Error("Unable to spawn process with " + this.player));
103789
103789
  return null;
@@ -107776,9 +107776,9 @@ var require_nodefs_handler = __commonJS((exports, module) => {
107776
107776
  if (this.fsw.closed) {
107777
107777
  return;
107778
107778
  }
107779
- const dirname21 = sysPath.dirname(file2);
107779
+ const dirname22 = sysPath.dirname(file2);
107780
107780
  const basename4 = sysPath.basename(file2);
107781
- const parent = this.fsw._getWatchedDir(dirname21);
107781
+ const parent = this.fsw._getWatchedDir(dirname22);
107782
107782
  let prevStats = stats2;
107783
107783
  if (parent.has(basename4))
107784
107784
  return;
@@ -107803,7 +107803,7 @@ var require_nodefs_handler = __commonJS((exports, module) => {
107803
107803
  prevStats = newStats2;
107804
107804
  }
107805
107805
  } catch (error3) {
107806
- this.fsw._remove(dirname21, basename4);
107806
+ this.fsw._remove(dirname22, basename4);
107807
107807
  }
107808
107808
  } else if (parent.has(basename4)) {
107809
107809
  const at2 = newStats.atimeMs;
@@ -157746,182 +157746,6 @@ var pubsubFeature = defineFeature({
157746
157746
  var import_deepmerge3 = __toESM(require_cjs2(), 1);
157747
157747
  import { relative as relative4 } from "path";
157748
157748
  import { minutes as minutes7, seconds as seconds5, toSeconds as toSeconds6 } from "@awsless/duration";
157749
-
157750
- // src/dev/servers/sqs.ts
157751
- import { createHash as createHash15, randomUUID as randomUUID5 } from "crypto";
157752
- import { createServer as createServer5 } from "http";
157753
- var md5 = (value) => createHash15("md5").update(value).digest("hex");
157754
- var md5OfAttributes = (attributes) => {
157755
- const parts = [];
157756
- const push = (value) => {
157757
- const data = Buffer.from(value, "utf8");
157758
- const size2 = Buffer.alloc(4);
157759
- size2.writeUInt32BE(data.length);
157760
- parts.push(size2, data);
157761
- };
157762
- for (const name of Object.keys(attributes).sort()) {
157763
- const attribute = attributes[name];
157764
- push(name);
157765
- push(attribute.DataType ?? "String");
157766
- if (typeof attribute.BinaryValue === "string") {
157767
- const data = Buffer.from(attribute.BinaryValue, "base64");
157768
- const size2 = Buffer.alloc(4);
157769
- size2.writeUInt32BE(data.length);
157770
- parts.push(Buffer.from([2]), size2, data);
157771
- } else {
157772
- const data = Buffer.from(attribute.StringValue ?? "", "utf8");
157773
- const size2 = Buffer.alloc(4);
157774
- size2.writeUInt32BE(data.length);
157775
- parts.push(Buffer.from([1]), size2, data);
157776
- }
157777
- }
157778
- return md5(Buffer.concat(parts));
157779
- };
157780
- var formatEventAttributes = (attributes) => {
157781
- const list2 = {};
157782
- for (const [name, attribute] of Object.entries(attributes)) {
157783
- list2[name] = {
157784
- dataType: attribute.DataType ?? "String",
157785
- stringValue: attribute.StringValue,
157786
- binaryValue: attribute.BinaryValue
157787
- };
157788
- }
157789
- return list2;
157790
- };
157791
- var createSqsServer = (props) => {
157792
- let server;
157793
- let closeServer;
157794
- let dispatch;
157795
- let reportFailure;
157796
- const queueFromUrl = (url) => {
157797
- const name = url?.split("/").filter(Boolean).at(-1);
157798
- return name && props.queues.has(name) ? name : undefined;
157799
- };
157800
- const deliver = (queue, input, messageId) => {
157801
- const now = Date.now();
157802
- const event = {
157803
- Records: [
157804
- {
157805
- messageId,
157806
- receiptHandle: randomUUID5(),
157807
- body: input.MessageBody,
157808
- attributes: {
157809
- ApproximateReceiveCount: "1",
157810
- SentTimestamp: String(now),
157811
- SenderId: "local",
157812
- ApproximateFirstReceiveTimestamp: String(now)
157813
- },
157814
- messageAttributes: formatEventAttributes(input.MessageAttributes ?? {}),
157815
- md5OfBody: md5(input.MessageBody ?? ""),
157816
- eventSource: "aws:sqs",
157817
- eventSourceARN: `arn:aws:sqs:${props.region}:${props.accountId}:${queue}`,
157818
- awsRegion: props.region
157819
- }
157820
- ]
157821
- };
157822
- const send = () => {
157823
- dispatch?.(event).catch((error3) => {
157824
- let body = input.MessageBody;
157825
- try {
157826
- body = JSON.parse(input.MessageBody ?? "");
157827
- } catch (_3) {}
157828
- reportFailure?.({
157829
- kind: "queue",
157830
- routeKey: props.queues.get(queue),
157831
- event: body,
157832
- error: error3,
157833
- queue: { name: queue }
157834
- });
157835
- });
157836
- };
157837
- if (input.DelaySeconds) {
157838
- setTimeout(send, input.DelaySeconds * 1000);
157839
- } else {
157840
- setImmediate(send);
157841
- }
157842
- };
157843
- const sendResult = (input, messageId) => {
157844
- return {
157845
- MessageId: messageId,
157846
- MD5OfMessageBody: md5(input.MessageBody ?? ""),
157847
- ...input.MessageAttributes && Object.keys(input.MessageAttributes).length > 0 ? { MD5OfMessageAttributes: md5OfAttributes(input.MessageAttributes) } : {}
157848
- };
157849
- };
157850
- const actions = {
157851
- GetQueueUrl(input) {
157852
- return {
157853
- QueueUrl: `http://sqs.local/${props.accountId}/${input.QueueName}`
157854
- };
157855
- },
157856
- SendMessage(input) {
157857
- const queue = queueFromUrl(input.QueueUrl);
157858
- if (!queue) {
157859
- throw new Error(`Unknown local queue: ${input.QueueUrl}`);
157860
- }
157861
- const messageId = randomUUID5();
157862
- deliver(queue, input, messageId);
157863
- return sendResult(input, messageId);
157864
- },
157865
- SendMessageBatch(input) {
157866
- const queue = queueFromUrl(input.QueueUrl);
157867
- if (!queue) {
157868
- throw new Error(`Unknown local queue: ${input.QueueUrl}`);
157869
- }
157870
- return {
157871
- Successful: (input.Entries ?? []).map((entry) => {
157872
- const messageId = randomUUID5();
157873
- deliver(queue, entry, messageId);
157874
- return {
157875
- Id: entry.Id,
157876
- ...sendResult(entry, messageId)
157877
- };
157878
- }),
157879
- Failed: []
157880
- };
157881
- }
157882
- };
157883
- return {
157884
- connect(dispatchFn, reportFailureFn) {
157885
- dispatch = dispatchFn;
157886
- reportFailure = reportFailureFn;
157887
- },
157888
- async listen(port = 0) {
157889
- server = createServer5((req, res) => {
157890
- const chunks = [];
157891
- req.on("data", (chunk3) => chunks.push(chunk3));
157892
- req.on("end", () => {
157893
- const target2 = String(req.headers["x-amz-target"] ?? "");
157894
- const action = actions[target2.split(".").at(-1) ?? ""];
157895
- try {
157896
- if (!action) {
157897
- throw new Error(`The local dev sqs emulator does not support: ${target2}`);
157898
- }
157899
- const result = action(JSON.parse(Buffer.concat(chunks).toString() || "{}"));
157900
- res.writeHead(200, { "content-type": "application/x-amz-json-1.0" });
157901
- res.end(JSON.stringify(result));
157902
- } catch (error3) {
157903
- res.writeHead(400, { "content-type": "application/x-amz-json-1.0" });
157904
- res.end(JSON.stringify({
157905
- __type: "InvalidAction",
157906
- message: error3 instanceof Error ? error3.message : String(error3)
157907
- }));
157908
- }
157909
- });
157910
- });
157911
- await new Promise((resolve3, reject) => {
157912
- server.once("error", reject);
157913
- closeServer = trackConnections(server);
157914
- server.listen(port, "127.0.0.1", () => resolve3());
157915
- });
157916
- return server.address().port;
157917
- },
157918
- stop() {
157919
- return closeServer?.() ?? Promise.resolve();
157920
- }
157921
- };
157922
- };
157923
-
157924
- // src/feature/queue/index.ts
157925
157749
  var typeGenCode5 = `
157926
157750
  import {
157927
157751
  SendMessageOptions,
@@ -157958,7 +157782,7 @@ var queueFeature = defineFeature({
157958
157782
  if (queues.length === 0) {
157959
157783
  return;
157960
157784
  }
157961
- const queueRoutes = new Map;
157785
+ const { port, queues: registry } = await ctx.useSqs();
157962
157786
  const named = queues.map(({ stackName, id }) => {
157963
157787
  const name = `${formatLocalResourceName({
157964
157788
  appName: ctx.appConfig.name,
@@ -157966,19 +157790,9 @@ var queueFeature = defineFeature({
157966
157790
  resourceType: "queue",
157967
157791
  resourceName: id
157968
157792
  })}.fifo`;
157969
- queueRoutes.set(name, formatRouteKey(stackName, "queue", id));
157793
+ registry.set(name, formatRouteKey(stackName, "queue", id));
157970
157794
  return { stackName, id, name };
157971
157795
  });
157972
- const { server, port } = await ctx.keep("shim:sqs", queueRoutes, async () => {
157973
- const server2 = createSqsServer({
157974
- region: ctx.appConfig.region,
157975
- accountId: "000000000000",
157976
- queues: queueRoutes
157977
- });
157978
- const port2 = await server2.listen();
157979
- return { value: { server: server2, port: port2 }, stop: () => server2.stop() };
157980
- });
157981
- ctx.addEnv("AWS_ENDPOINT_URL_SQS", `http://127.0.0.1:${port}`);
157982
157796
  for (const { stackName, id, name } of named) {
157983
157797
  ctx.addEnv(`QUEUE_${constantCase(stackName)}_${constantCase(id)}_URL`, `http://127.0.0.1:${port}/000000000000/${name}`);
157984
157798
  ctx.registerResource({
@@ -157989,12 +157803,6 @@ var queueFeature = defineFeature({
157989
157803
  detail: name
157990
157804
  });
157991
157805
  }
157992
- ctx.registerServer({
157993
- name: "sqs",
157994
- start({ dispatch, reportFailure }) {
157995
- server.connect(dispatch, reportFailure);
157996
- }
157997
- });
157998
157806
  },
157999
157807
  async onTypeGen(ctx) {
158000
157808
  const gen = new TypeFile("awsless");
@@ -158081,7 +157889,7 @@ var queueFeature = defineFeature({
158081
157889
  });
158082
157890
 
158083
157891
  // src/dev/servers/rest.ts
158084
- import { createServer as createServer6 } from "http";
157892
+ import { createServer as createServer5 } from "http";
158085
157893
 
158086
157894
  // src/dev/router.ts
158087
157895
  var compileRoutes = (routes) => {
@@ -158448,7 +158256,7 @@ var createRestServer = (props) => {
158448
158256
  dispatch = dispatchFn;
158449
158257
  },
158450
158258
  async listen(port = 0) {
158451
- server = createServer6((req, res) => {
158259
+ server = createServer5((req, res) => {
158452
158260
  handle(req, res).catch((error3) => {
158453
158261
  res.writeHead(500, { "content-type": "text/plain" });
158454
158262
  res.end(error3 instanceof Error ? `${error3.name}: ${error3.message}` : String(error3));
@@ -158982,7 +158790,7 @@ var searchFeature = defineFeature({
158982
158790
  });
158983
158791
 
158984
158792
  // src/feature/site/index.ts
158985
- import { createHash as createHash17 } from "crypto";
158793
+ import { createHash as createHash16 } from "crypto";
158986
158794
  import { dirname as dirname17, join as join35 } from "path";
158987
158795
 
158988
158796
  // src/util/cache.ts
@@ -159339,7 +159147,7 @@ ${(errors3.trim() || output.trim()).slice(-2000)}`);
159339
159147
  }).sort();
159340
159148
  const hashes = files.map((file2) => $hash(join35(staticDir, file2)));
159341
159149
  const version2 = $combine(...hashes).pipe((hashes2) => {
159342
- const hash2 = createHash17("sha1");
159150
+ const hash2 = createHash16("sha1");
159343
159151
  for (const [index, file2] of files.entries()) {
159344
159152
  hash2.update(file2);
159345
159153
  hash2.update(hashes2[index]);
@@ -159944,7 +159752,7 @@ var tableFeature = defineFeature({
159944
159752
  import { relative as relative6 } from "path";
159945
159753
 
159946
159754
  // src/dev/servers/scheduler.ts
159947
- import { createServer as createServer7 } from "http";
159755
+ import { createServer as createServer6 } from "http";
159948
159756
  var createSchedulerServer = () => {
159949
159757
  let server;
159950
159758
  let closeServer;
@@ -159956,7 +159764,7 @@ var createSchedulerServer = () => {
159956
159764
  reportFailure = reportFailureFn;
159957
159765
  },
159958
159766
  async listen(port = 0) {
159959
- server = createServer7((req, res) => {
159767
+ server = createServer6((req, res) => {
159960
159768
  const chunks = [];
159961
159769
  req.on("data", (chunk3) => chunks.push(chunk3));
159962
159770
  req.on("end", () => {
@@ -160593,7 +160401,7 @@ var layerFeature = defineFeature({
160593
160401
  // src/feature/image/index.ts
160594
160402
  import { toDays as toDays9 } from "@awsless/duration";
160595
160403
  import { formatRouteEnvName as formatRouteEnvName3 } from "awsless";
160596
- import { createHash as createHash18 } from "crypto";
160404
+ import { createHash as createHash17 } from "crypto";
160597
160405
  import { readFile as readFile17 } from "fs/promises";
160598
160406
  import { dirname as dirname18, join as join40 } from "path";
160599
160407
  import { fileURLToPath as fileURLToPath13 } from "url";
@@ -160677,7 +160485,7 @@ var imageFeature = defineFeature({
160677
160485
  });
160678
160486
  ctx.registerBuild("image", layerId, async (build2) => {
160679
160487
  const file2 = await readFile17(path6);
160680
- const fingerprint = createHash18("sha1").update(file2).digest("hex");
160488
+ const fingerprint = createHash17("sha1").update(file2).digest("hex");
160681
160489
  return build2(fingerprint, async (write2) => {
160682
160490
  await Promise.all([
160683
160491
  write2("HASH", fingerprint),
@@ -160958,10 +160766,10 @@ import { toDays as toDays11, toSeconds as toSeconds10 } from "@awsless/duration"
160958
160766
  import { stringify as stringify2 } from "@awsless/json";
160959
160767
  import { generateFileHash as generateFileHash3 } from "@awsless/ts-file-cache";
160960
160768
  var import_deepmerge4 = __toESM(require_cjs2(), 1);
160961
- import { createHash as createHash20 } from "crypto";
160769
+ import { createHash as createHash19 } from "crypto";
160962
160770
 
160963
160771
  // src/feature/job/build/executable.ts
160964
- import { createHash as createHash19 } from "crypto";
160772
+ import { createHash as createHash18 } from "crypto";
160965
160773
  import { readFile as readFile18, writeFile as writeFile9 } from "fs/promises";
160966
160774
  import { join as join43, resolve as resolve4 } from "path";
160967
160775
  var buildJobExecutable = async (input, outputPath, architecture) => {
@@ -161019,7 +160827,7 @@ ${result.logs?.map((log) => log.message).join(`
161019
160827
  }
161020
160828
  const file2 = await readFile18(filePath);
161021
160829
  return {
161022
- hash: createHash19("sha1").update(file2).update(target2).digest("hex"),
160830
+ hash: createHash18("sha1").update(file2).update(target2).digest("hex"),
161023
160831
  file: file2
161024
160832
  };
161025
160833
  };
@@ -161282,7 +161090,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
161282
161090
  variables.AWS_ACCOUNT_ID = ctx.accountId;
161283
161091
  variables.STACK = ctx.stackConfig.name;
161284
161092
  variables.CODE_HASH = code.sourceHash;
161285
- variables.JOB_CONFIG_HASH = createHash20("sha1").update(stringify2(props)).digest("hex");
161093
+ variables.JOB_CONFIG_HASH = createHash19("sha1").update(stringify2(props)).digest("hex");
161286
161094
  if (props.environment) {
161287
161095
  for (const [key, value] of Object.entries(props.environment)) {
161288
161096
  variables[key] = value;
@@ -161474,13 +161282,124 @@ var jobFeature = defineFeature({
161474
161282
  // src/feature/instance/index.ts
161475
161283
  import { days as days11, seconds as seconds6, toSeconds as toSeconds12 } from "@awsless/duration";
161476
161284
 
161285
+ // src/feature/instance/dev.ts
161286
+ import { spawn as spawn5 } from "child_process";
161287
+ var import_deepmerge6 = __toESM(require_cjs2(), 1);
161288
+ import { dirname as dirname20, join as join44 } from "path";
161289
+ var instanceOnDev = async (ctx) => {
161290
+ const instances = ctx.stackConfigs.flatMap((stackConfig) => {
161291
+ return Object.entries(stackConfig.instances ?? {}).map(([id, props]) => ({ stackConfig, id, props }));
161292
+ });
161293
+ if (instances.length === 0) {
161294
+ return;
161295
+ }
161296
+ const { port, queues } = await ctx.useSqs();
161297
+ for (const { stackConfig, id, props } of instances) {
161298
+ const name = formatLocalResourceName({
161299
+ appName: ctx.appConfig.name,
161300
+ stackName: stackConfig.name,
161301
+ resourceType: "instance",
161302
+ resourceName: id
161303
+ });
161304
+ queues.set(name, undefined);
161305
+ const queueUrl = `http://127.0.0.1:${port}/000000000000/${name}`;
161306
+ ctx.addEnv(`INSTANCE_${constantCase(stackConfig.name)}_${constantCase(id)}_URL`, queueUrl);
161307
+ const channel = `instance:${stackConfig.name}:${id}`;
161308
+ const healthPort = await findFreePort();
161309
+ const merged = import_deepmerge6.default(ctx.appConfig.instance ?? {}, props);
161310
+ const file2 = merged.code.file;
161311
+ const cwd2 = join44(directories.root, dirname20(stackConfig.file));
161312
+ let child;
161313
+ let stopping = false;
161314
+ let restarting = false;
161315
+ let boot;
161316
+ const restart = async (reason) => {
161317
+ if (!boot || restarting) {
161318
+ return;
161319
+ }
161320
+ restarting = true;
161321
+ ctx.emitEvent(channel, { date: Date.now(), line: reason });
161322
+ try {
161323
+ if (child) {
161324
+ stopping = true;
161325
+ await stopChild(child);
161326
+ stopping = false;
161327
+ }
161328
+ boot();
161329
+ } finally {
161330
+ restarting = false;
161331
+ }
161332
+ };
161333
+ ctx.registerServer({
161334
+ name: `instance ${id}`,
161335
+ async start({ env: env4 }) {
161336
+ const tail = [];
161337
+ boot = () => {
161338
+ child = spawn5("bun", [file2], {
161339
+ cwd: cwd2,
161340
+ stdio: ["ignore", "pipe", "pipe"],
161341
+ env: {
161342
+ ...process.env,
161343
+ ...env4,
161344
+ ...merged.environment ?? {},
161345
+ STACK: stackConfig.name,
161346
+ PORT: String(healthPort),
161347
+ PATH: binPath(cwd2)
161348
+ }
161349
+ });
161350
+ const capture = (chunk3) => {
161351
+ for (const raw of chunk3.toString().split(`
161352
+ `)) {
161353
+ const line = stripAnsi2(raw);
161354
+ if (line.trim() === "") {
161355
+ continue;
161356
+ }
161357
+ tail.push(line);
161358
+ ctx.emitEvent(channel, { date: Date.now(), line });
161359
+ }
161360
+ while (tail.length > 20) {
161361
+ tail.shift();
161362
+ }
161363
+ };
161364
+ child.stdout?.on("data", capture);
161365
+ child.stderr?.on("data", capture);
161366
+ child.on("exit", (code) => {
161367
+ if (!stopping && code !== null && code !== 0) {
161368
+ logs_exports.error(`The instance "${id}" exited with code ${code}:
161369
+ ${tail.join(`
161370
+ `)}`);
161371
+ ctx.emitEvent(channel, { date: Date.now(), line: `Exited with code ${code}`, error: true });
161372
+ }
161373
+ });
161374
+ };
161375
+ boot();
161376
+ },
161377
+ async stop() {
161378
+ stopping = true;
161379
+ if (child) {
161380
+ await stopChild(child);
161381
+ }
161382
+ }
161383
+ });
161384
+ ctx.registerResource({
161385
+ kind: "instance",
161386
+ stack: stackConfig.name,
161387
+ id,
161388
+ detail: `http://localhost:${healthPort}`,
161389
+ queueUrl,
161390
+ channel,
161391
+ restart: () => restart("Restarting...")
161392
+ });
161393
+ }
161394
+ };
161395
+
161477
161396
  // src/feature/instance/util.ts
161478
161397
  import { toDays as toDays12, toSeconds as toSeconds11 } from "@awsless/duration";
161479
161398
  import { stringify as stringify3 } from "@awsless/json";
161480
161399
  import { generateFileHash as generateFileHash4 } from "@awsless/ts-file-cache";
161481
- var import_deepmerge6 = __toESM(require_cjs2(), 1);
161482
- import { createHash as createHash21 } from "crypto";
161483
- import { join as join44 } from "path";
161400
+ var import_deepmerge7 = __toESM(require_cjs2(), 1);
161401
+ import { createHash as createHash20 } from "crypto";
161402
+ import { join as join45 } from "path";
161484
161403
  var createFargateTask = (parentGroup, ctx, ns, id, local) => {
161485
161404
  const group4 = new Group(parentGroup, "instance", ns);
161486
161405
  const name = formatLocalResourceName({
@@ -161490,7 +161409,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
161490
161409
  resourceName: id
161491
161410
  });
161492
161411
  const shortName = shortId(`${ctx.app.name}:${ctx.stack.name}:${ns}:${id}:${ctx.appId}`);
161493
- const props = import_deepmerge6.default(ctx.appConfig.instance, local);
161412
+ const props = import_deepmerge7.default(ctx.appConfig.instance, local);
161494
161413
  const image = props.image || (props.architecture === "arm64" ? "public.ecr.aws/aws-cli/aws-cli:arm64" : "public.ecr.aws/aws-cli/aws-cli:amd64");
161495
161414
  ctx.registerBuild("instance", name, async (build2, { workspace }) => {
161496
161415
  const fingerprint = await generateFileHash4(workspace, local.code.file);
@@ -161682,7 +161601,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
161682
161601
  healthCheck: props.healthCheck ? {
161683
161602
  command: [
161684
161603
  "CMD-SHELL",
161685
- `curl -f http://${join44("localhost", props.healthCheck.path)} || exit 1`
161604
+ `curl -f http://${join45("localhost", props.healthCheck.path)} || exit 1`
161686
161605
  ],
161687
161606
  interval: toSeconds11(props.healthCheck.interval),
161688
161607
  retries: props.healthCheck.retries,
@@ -161769,7 +161688,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
161769
161688
  variables.AWS_ACCOUNT_ID = ctx.accountId;
161770
161689
  variables.STACK = ctx.stackConfig.name;
161771
161690
  variables.CODE_HASH = code.sourceHash;
161772
- variables.INSTANCE_CONFIG_HASH = createHash21("sha1").update(stringify3(props)).digest("hex");
161691
+ variables.INSTANCE_CONFIG_HASH = createHash20("sha1").update(stringify3(props)).digest("hex");
161773
161692
  if (props.environment) {
161774
161693
  for (const [key, value] of Object.entries(props.environment)) {
161775
161694
  variables[key] = value;
@@ -161804,6 +161723,7 @@ type TestMockEntry = MockBuilder & MockObject
161804
161723
  `;
161805
161724
  var instanceFeature = defineFeature({
161806
161725
  name: "instance",
161726
+ onDev: instanceOnDev,
161807
161727
  async onTypeGen(ctx) {
161808
161728
  const gen = new TypeFile("awsless");
161809
161729
  const resources = new TypeObject(1);
@@ -164264,12 +164184,12 @@ import { DynamoDBClient as DynamoDBClient4 } from "@awsless/dynamodb";
164264
164184
  // src/cli/ui/complex/run-tests.ts
164265
164185
  var import_wildstring2 = __toESM(require_wildstring(), 1);
164266
164186
  import { mkdir as mkdir7, readFile as readFile19, writeFile as writeFile10 } from "fs/promises";
164267
- import { join as join46 } from "path";
164187
+ import { join as join47 } from "path";
164268
164188
  import { parse as parse7, stringify as stringify4 } from "@awsless/json";
164269
164189
  import { generateFolderHash, loadWorkspace as loadWorkspace2 } from "@awsless/ts-file-cache";
164270
164190
 
164271
164191
  // src/test/start.ts
164272
- import { dirname as dirname20, join as join45 } from "path";
164192
+ import { dirname as dirname21, join as join46 } from "path";
164273
164193
  import { fileURLToPath as fileURLToPath15 } from "url";
164274
164194
  import { configDefaults } from "vitest/config";
164275
164195
  import { startVitest } from "vitest/node";
@@ -164323,7 +164243,7 @@ ${statements.join(`
164323
164243
  class NullReporter {
164324
164244
  }
164325
164245
  var startTest = async (props) => {
164326
- const __dirname4 = dirname20(fileURLToPath15(import.meta.url));
164246
+ const __dirname4 = dirname21(fileURLToPath15(import.meta.url));
164327
164247
  const startTime = process.hrtime.bigint();
164328
164248
  process.noDeprecation = true;
164329
164249
  const nodeEnv = process.env["NODE_ENV"];
@@ -164345,7 +164265,7 @@ var startTest = async (props) => {
164345
164265
  reporters: [new NullReporter],
164346
164266
  env: props.env,
164347
164267
  setupFiles: [
164348
- join45(__dirname4, "test-global-setup.js")
164268
+ join46(__dirname4, "test-global-setup.js")
164349
164269
  ]
164350
164270
  }, {
164351
164271
  logLevel: "silent",
@@ -164501,7 +164421,7 @@ var logTestErrors = (event) => {
164501
164421
  };
164502
164422
  var runTest = async (stack, dir, filters, workspace, opts) => {
164503
164423
  await mkdir7(directories.test, { recursive: true });
164504
- const file2 = join46(directories.test, `${stack}.json`);
164424
+ const file2 = join47(directories.test, `${stack}.json`);
164505
164425
  const fingerprint = await generateFolderHash(workspace, dir);
164506
164426
  if (!process.env.NO_CACHE) {
164507
164427
  const exists4 = await fileExist(file2);
@@ -165411,12 +165331,12 @@ var auth = (program3) => {
165411
165331
 
165412
165332
  // src/cli/command/bind.ts
165413
165333
  import { readFile as readFile20 } from "fs/promises";
165414
- import { join as join47 } from "path";
165334
+ import { join as join48 } from "path";
165415
165335
  var bind = (program3) => {
165416
165336
  program3.command("bind").argument("[command...]", "The command to execute").option("--config <string...>", "List of config values that will be accessable", (v3) => v3.split(",")).option("--local", "Bind against the running local dev environment instead of the deployed app").description(`Bind your site environment variables to a command`).action(async (commands5 = [], opts) => {
165417
165337
  await layout("bind", async ({ appConfig, stackConfigs }) => {
165418
165338
  if (opts.local) {
165419
- const file2 = join47(directories.output, "local", "env.json");
165339
+ const file2 = join48(directories.output, "local", "env.json");
165420
165340
  let env5;
165421
165341
  try {
165422
165342
  env5 = JSON.parse(await readFile20(file2, "utf8"));
@@ -166210,17 +166130,17 @@ var watchConfig = async (options, resolve5, reject) => {
166210
166130
  // src/dev/index.ts
166211
166131
  import { loadWorkspace as loadWorkspace3 } from "@awsless/ts-file-cache";
166212
166132
  import { mkdir as mkdir10, rm as rm12, writeFile as writeFile14 } from "fs/promises";
166213
- import { join as join54 } from "path";
166133
+ import { join as join55 } from "path";
166214
166134
 
166215
166135
  // src/dev/context.ts
166216
166136
  import { DynamoDBServer } from "@awsless/dynamodb-server";
166217
- import { join as join49 } from "path";
166137
+ import { join as join50 } from "path";
166218
166138
 
166219
166139
  // src/dev/servers/s3.ts
166220
- import { createHash as createHash22 } from "crypto";
166221
- import { createServer as createServer8 } from "http";
166140
+ import { createHash as createHash21 } from "crypto";
166141
+ import { createServer as createServer7 } from "http";
166222
166142
  import { mkdir as mkdir8, readdir as readdir8, readFile as readFile21, rm as rm10, stat as stat7, writeFile as writeFile11 } from "fs/promises";
166223
- import { dirname as dirname21, join as join48, relative as relative9, sep as sep4 } from "path";
166143
+ import { dirname as dirname22, join as join49, relative as relative9, sep as sep4 } from "path";
166224
166144
  var readBody2 = (req) => {
166225
166145
  return new Promise((resolve5, reject) => {
166226
166146
  const chunks = [];
@@ -166290,8 +166210,8 @@ var createS3Server = (props) => {
166290
166210
  res.end(xmlError("InvalidRequest", "Missing bucket name"));
166291
166211
  return;
166292
166212
  }
166293
- const bucketDir = join48(props.root, bucket);
166294
- const file2 = join48(bucketDir, ...keyParts);
166213
+ const bucketDir = join49(props.root, bucket);
166214
+ const file2 = join49(bucketDir, ...keyParts);
166295
166215
  if (relative9(bucketDir, file2).startsWith("..")) {
166296
166216
  res.writeHead(400, { "content-type": "application/xml" });
166297
166217
  res.end(xmlError("InvalidRequest", "Invalid object key"));
@@ -166308,7 +166228,7 @@ var createS3Server = (props) => {
166308
166228
  return;
166309
166229
  }
166310
166230
  for (const entry of entries2) {
166311
- const path6 = join48(dir, entry.name);
166231
+ const path6 = join49(dir, entry.name);
166312
166232
  if (entry.isDirectory()) {
166313
166233
  await walk2(path6);
166314
166234
  } else {
@@ -166334,9 +166254,9 @@ var createS3Server = (props) => {
166334
166254
  if (typeof encoding === "string" && encoding.includes("aws-chunked") || typeof sha === "string" && sha.startsWith("STREAMING")) {
166335
166255
  body = decodeAwsChunked(body);
166336
166256
  }
166337
- await mkdir8(dirname21(file2), { recursive: true });
166257
+ await mkdir8(dirname22(file2), { recursive: true });
166338
166258
  await writeFile11(file2, body);
166339
- const eTag = createHash22("md5").update(body).digest("hex");
166259
+ const eTag = createHash21("md5").update(body).digest("hex");
166340
166260
  res.writeHead(200, { etag: `"${eTag}"` });
166341
166261
  res.end();
166342
166262
  notify("ObjectCreated:Put", bucket, key, body.length, eTag);
@@ -166353,7 +166273,7 @@ var createS3Server = (props) => {
166353
166273
  }
166354
166274
  res.writeHead(200, {
166355
166275
  "content-length": body.length,
166356
- etag: `"${createHash22("md5").update(body).digest("hex")}"`
166276
+ etag: `"${createHash21("md5").update(body).digest("hex")}"`
166357
166277
  });
166358
166278
  res.end(req.method === "HEAD" ? undefined : body);
166359
166279
  return;
@@ -166374,7 +166294,7 @@ var createS3Server = (props) => {
166374
166294
  reportFailure = reportFailureFn;
166375
166295
  },
166376
166296
  async listen(port = 0) {
166377
- server = createServer8((req, res) => {
166297
+ server = createServer7((req, res) => {
166378
166298
  handle(req, res).catch((error3) => {
166379
166299
  res.writeHead(500, { "content-type": "application/xml" });
166380
166300
  res.end(xmlError("InternalError", error3 instanceof Error ? error3.message : String(error3)));
@@ -166393,6 +166313,323 @@ var createS3Server = (props) => {
166393
166313
  };
166394
166314
  };
166395
166315
 
166316
+ // src/dev/servers/sqs.ts
166317
+ import { createHash as createHash22, randomUUID as randomUUID5 } from "crypto";
166318
+ import { createServer as createServer8 } from "http";
166319
+ var md5 = (value) => createHash22("md5").update(value).digest("hex");
166320
+ var md5OfAttributes = (attributes) => {
166321
+ const parts = [];
166322
+ const push = (value) => {
166323
+ const data = Buffer.from(value, "utf8");
166324
+ const size2 = Buffer.alloc(4);
166325
+ size2.writeUInt32BE(data.length);
166326
+ parts.push(size2, data);
166327
+ };
166328
+ for (const name of Object.keys(attributes).sort()) {
166329
+ const attribute = attributes[name];
166330
+ push(name);
166331
+ push(attribute.DataType ?? "String");
166332
+ if (typeof attribute.BinaryValue === "string") {
166333
+ const data = Buffer.from(attribute.BinaryValue, "base64");
166334
+ const size2 = Buffer.alloc(4);
166335
+ size2.writeUInt32BE(data.length);
166336
+ parts.push(Buffer.from([2]), size2, data);
166337
+ } else {
166338
+ const data = Buffer.from(attribute.StringValue ?? "", "utf8");
166339
+ const size2 = Buffer.alloc(4);
166340
+ size2.writeUInt32BE(data.length);
166341
+ parts.push(Buffer.from([1]), size2, data);
166342
+ }
166343
+ }
166344
+ return md5(Buffer.concat(parts));
166345
+ };
166346
+ var formatEventAttributes = (attributes) => {
166347
+ const list3 = {};
166348
+ for (const [name, attribute] of Object.entries(attributes)) {
166349
+ list3[name] = {
166350
+ dataType: attribute.DataType ?? "String",
166351
+ stringValue: attribute.StringValue,
166352
+ binaryValue: attribute.BinaryValue
166353
+ };
166354
+ }
166355
+ return list3;
166356
+ };
166357
+ var createSqsServer = (props) => {
166358
+ let server;
166359
+ let closeServer;
166360
+ let dispatch;
166361
+ let reportFailure;
166362
+ let boundPort = 0;
166363
+ const stores = new Map;
166364
+ const queueFromUrl = (url) => {
166365
+ const name = url?.split("/").filter(Boolean).at(-1);
166366
+ return name && props.queues.has(name) ? name : undefined;
166367
+ };
166368
+ const storeOf = (queue) => {
166369
+ if (!stores.has(queue)) {
166370
+ stores.set(queue, []);
166371
+ }
166372
+ return stores.get(queue);
166373
+ };
166374
+ const deliver = (queue, input, messageId) => {
166375
+ const now = Date.now();
166376
+ if (props.queues.get(queue) === undefined) {
166377
+ storeOf(queue).push({
166378
+ id: messageId,
166379
+ body: input.MessageBody ?? "",
166380
+ attributes: input.MessageAttributes ?? {},
166381
+ sentAt: now,
166382
+ receiveCount: 0,
166383
+ visibleAt: now + (input.DelaySeconds ?? 0) * 1000
166384
+ });
166385
+ return;
166386
+ }
166387
+ const event = {
166388
+ Records: [
166389
+ {
166390
+ messageId,
166391
+ receiptHandle: randomUUID5(),
166392
+ body: input.MessageBody,
166393
+ attributes: {
166394
+ ApproximateReceiveCount: "1",
166395
+ SentTimestamp: String(now),
166396
+ SenderId: "local",
166397
+ ApproximateFirstReceiveTimestamp: String(now)
166398
+ },
166399
+ messageAttributes: formatEventAttributes(input.MessageAttributes ?? {}),
166400
+ md5OfBody: md5(input.MessageBody ?? ""),
166401
+ eventSource: "aws:sqs",
166402
+ eventSourceARN: `arn:aws:sqs:${props.region}:${props.accountId}:${queue}`,
166403
+ awsRegion: props.region
166404
+ }
166405
+ ]
166406
+ };
166407
+ const send = () => {
166408
+ dispatch?.(event).catch((error3) => {
166409
+ let body = input.MessageBody;
166410
+ try {
166411
+ body = JSON.parse(input.MessageBody ?? "");
166412
+ } catch (_4) {}
166413
+ reportFailure?.({
166414
+ kind: "queue",
166415
+ routeKey: props.queues.get(queue),
166416
+ event: body,
166417
+ error: error3,
166418
+ queue: { name: queue }
166419
+ });
166420
+ });
166421
+ };
166422
+ if (input.DelaySeconds) {
166423
+ setTimeout(send, input.DelaySeconds * 1000);
166424
+ } else {
166425
+ setImmediate(send);
166426
+ }
166427
+ };
166428
+ const sendResult = (input, messageId) => {
166429
+ return {
166430
+ MessageId: messageId,
166431
+ MD5OfMessageBody: md5(input.MessageBody ?? ""),
166432
+ ...input.MessageAttributes && Object.keys(input.MessageAttributes).length > 0 ? { MD5OfMessageAttributes: md5OfAttributes(input.MessageAttributes) } : {}
166433
+ };
166434
+ };
166435
+ const queueUrl = (name) => {
166436
+ return `http://127.0.0.1:${boundPort}/${props.accountId}/${name}`;
166437
+ };
166438
+ const findByReceipt = (queue, receipt) => {
166439
+ return storeOf(queue).find((message4) => message4.receipt === receipt);
166440
+ };
166441
+ const actions = {
166442
+ GetQueueUrl(input) {
166443
+ if (!input.QueueName || !props.queues.has(input.QueueName)) {
166444
+ throw new Error(`Unknown local queue: ${input.QueueName}`);
166445
+ }
166446
+ return {
166447
+ QueueUrl: queueUrl(input.QueueName)
166448
+ };
166449
+ },
166450
+ SendMessage(input) {
166451
+ const queue = queueFromUrl(input.QueueUrl);
166452
+ if (!queue) {
166453
+ throw new Error(`Unknown local queue: ${input.QueueUrl}`);
166454
+ }
166455
+ const messageId = randomUUID5();
166456
+ deliver(queue, input, messageId);
166457
+ return sendResult(input, messageId);
166458
+ },
166459
+ SendMessageBatch(input) {
166460
+ const queue = queueFromUrl(input.QueueUrl);
166461
+ if (!queue) {
166462
+ throw new Error(`Unknown local queue: ${input.QueueUrl}`);
166463
+ }
166464
+ return {
166465
+ Successful: (input.Entries ?? []).map((entry) => {
166466
+ const messageId = randomUUID5();
166467
+ deliver(queue, entry, messageId);
166468
+ return {
166469
+ Id: entry.Id,
166470
+ ...sendResult(entry, messageId)
166471
+ };
166472
+ }),
166473
+ Failed: []
166474
+ };
166475
+ },
166476
+ async ReceiveMessage(input, signal) {
166477
+ const queue = queueFromUrl(input.QueueUrl);
166478
+ if (!queue) {
166479
+ throw new Error(`Unknown local queue: ${input.QueueUrl}`);
166480
+ }
166481
+ const store = storeOf(queue);
166482
+ const deadline = Date.now() + (input.WaitTimeSeconds ?? 0) * 1000;
166483
+ while (true) {
166484
+ const now = Date.now();
166485
+ const visible = store.filter((message4) => message4.visibleAt <= now);
166486
+ if (visible.length > 0) {
166487
+ const batch2 = visible.slice(0, Math.max(1, input.MaxNumberOfMessages ?? 1));
166488
+ return {
166489
+ Messages: batch2.map((message4) => {
166490
+ message4.receipt = randomUUID5();
166491
+ message4.receiveCount += 1;
166492
+ message4.firstReceivedAt ??= now;
166493
+ message4.visibleAt = now + (input.VisibilityTimeout ?? 30) * 1000;
166494
+ return {
166495
+ MessageId: message4.id,
166496
+ ReceiptHandle: message4.receipt,
166497
+ Body: message4.body,
166498
+ MD5OfBody: md5(message4.body),
166499
+ Attributes: {
166500
+ ApproximateReceiveCount: String(message4.receiveCount),
166501
+ SentTimestamp: String(message4.sentAt),
166502
+ SenderId: "local",
166503
+ ApproximateFirstReceiveTimestamp: String(message4.firstReceivedAt)
166504
+ },
166505
+ ...Object.keys(message4.attributes).length > 0 ? {
166506
+ MessageAttributes: message4.attributes,
166507
+ MD5OfMessageAttributes: md5OfAttributes(message4.attributes)
166508
+ } : {}
166509
+ };
166510
+ })
166511
+ };
166512
+ }
166513
+ if (now >= deadline || signal.aborted) {
166514
+ return {};
166515
+ }
166516
+ await new Promise((resolve5) => setTimeout(resolve5, 100));
166517
+ }
166518
+ },
166519
+ DeleteMessage(input) {
166520
+ const queue = queueFromUrl(input.QueueUrl);
166521
+ if (!queue) {
166522
+ throw new Error(`Unknown local queue: ${input.QueueUrl}`);
166523
+ }
166524
+ const store = storeOf(queue);
166525
+ const index = store.findIndex((message4) => message4.receipt === input.ReceiptHandle);
166526
+ if (index >= 0) {
166527
+ store.splice(index, 1);
166528
+ }
166529
+ return {};
166530
+ },
166531
+ DeleteMessageBatch(input) {
166532
+ const queue = queueFromUrl(input.QueueUrl);
166533
+ if (!queue) {
166534
+ throw new Error(`Unknown local queue: ${input.QueueUrl}`);
166535
+ }
166536
+ const store = storeOf(queue);
166537
+ return {
166538
+ Successful: (input.Entries ?? []).map((entry) => {
166539
+ const index = store.findIndex((message4) => message4.receipt === entry.ReceiptHandle);
166540
+ if (index >= 0) {
166541
+ store.splice(index, 1);
166542
+ }
166543
+ return { Id: entry.Id };
166544
+ }),
166545
+ Failed: []
166546
+ };
166547
+ },
166548
+ ChangeMessageVisibility(input) {
166549
+ const queue = queueFromUrl(input.QueueUrl);
166550
+ if (!queue) {
166551
+ throw new Error(`Unknown local queue: ${input.QueueUrl}`);
166552
+ }
166553
+ const message4 = findByReceipt(queue, input.ReceiptHandle);
166554
+ if (message4) {
166555
+ message4.visibleAt = Date.now() + (input.VisibilityTimeout ?? 0) * 1000;
166556
+ }
166557
+ return {};
166558
+ },
166559
+ GetQueueAttributes(input) {
166560
+ const queue = queueFromUrl(input.QueueUrl);
166561
+ if (!queue) {
166562
+ throw new Error(`Unknown local queue: ${input.QueueUrl}`);
166563
+ }
166564
+ const now = Date.now();
166565
+ const store = storeOf(queue);
166566
+ return {
166567
+ Attributes: {
166568
+ QueueArn: `arn:aws:sqs:${props.region}:${props.accountId}:${queue}`,
166569
+ ApproximateNumberOfMessages: String(store.filter((m4) => m4.visibleAt <= now).length),
166570
+ ApproximateNumberOfMessagesNotVisible: String(store.filter((m4) => m4.visibleAt > now).length)
166571
+ }
166572
+ };
166573
+ },
166574
+ PurgeQueue(input) {
166575
+ const queue = queueFromUrl(input.QueueUrl);
166576
+ if (!queue) {
166577
+ throw new Error(`Unknown local queue: ${input.QueueUrl}`);
166578
+ }
166579
+ storeOf(queue).length = 0;
166580
+ return {};
166581
+ }
166582
+ };
166583
+ return {
166584
+ connect(dispatchFn, reportFailureFn) {
166585
+ dispatch = dispatchFn;
166586
+ reportFailure = reportFailureFn;
166587
+ },
166588
+ async listen(port = 0) {
166589
+ server = createServer8((req, res) => {
166590
+ const chunks = [];
166591
+ req.on("data", (chunk3) => chunks.push(chunk3));
166592
+ req.on("end", async () => {
166593
+ const target2 = String(req.headers["x-amz-target"] ?? "");
166594
+ const action = actions[target2.split(".").at(-1) ?? ""];
166595
+ const abort = new AbortController;
166596
+ res.on("close", () => abort.abort());
166597
+ try {
166598
+ if (!action) {
166599
+ throw new Error(`The local dev sqs emulator does not support: ${target2}`);
166600
+ }
166601
+ const result = await action(JSON.parse(Buffer.concat(chunks).toString() || "{}"), abort.signal);
166602
+ if (res.writableEnded || res.destroyed) {
166603
+ return;
166604
+ }
166605
+ res.writeHead(200, { "content-type": "application/x-amz-json-1.0" });
166606
+ res.end(JSON.stringify(result));
166607
+ } catch (error3) {
166608
+ if (res.writableEnded || res.destroyed) {
166609
+ return;
166610
+ }
166611
+ res.writeHead(400, { "content-type": "application/x-amz-json-1.0" });
166612
+ res.end(JSON.stringify({
166613
+ __type: "InvalidAction",
166614
+ message: error3 instanceof Error ? error3.message : String(error3)
166615
+ }));
166616
+ }
166617
+ });
166618
+ });
166619
+ await new Promise((resolve5, reject) => {
166620
+ server.once("error", reject);
166621
+ closeServer = trackConnections(server);
166622
+ server.listen(port, "127.0.0.1", () => resolve5());
166623
+ });
166624
+ boundPort = server.address().port;
166625
+ return boundPort;
166626
+ },
166627
+ stop() {
166628
+ return closeServer?.() ?? Promise.resolve();
166629
+ }
166630
+ };
166631
+ };
166632
+
166396
166633
  // src/dev/context.ts
166397
166634
  var createDevContext = (props) => {
166398
166635
  const env4 = {};
@@ -166447,7 +166684,7 @@ var createDevContext = (props) => {
166447
166684
  const value = await props.pool.keep("store", null, async () => {
166448
166685
  const rules = [];
166449
166686
  const server = createS3Server({
166450
- root: join49(directories.output, "local", "store"),
166687
+ root: join50(directories.output, "local", "store"),
166451
166688
  region: props.appConfig.region,
166452
166689
  rules
166453
166690
  });
@@ -166469,6 +166706,34 @@ var createDevContext = (props) => {
166469
166706
  })();
166470
166707
  return store;
166471
166708
  };
166709
+ let sqs;
166710
+ const useSqs = () => {
166711
+ sqs ??= (async () => {
166712
+ const value = await props.pool.keep("shim:sqs", null, async () => {
166713
+ const queues = new Map;
166714
+ const server = createSqsServer({
166715
+ region: props.appConfig.region,
166716
+ accountId: "000000000000",
166717
+ queues
166718
+ });
166719
+ const port = await server.listen();
166720
+ return {
166721
+ value: { server, port, queues },
166722
+ stop: () => server.stop()
166723
+ };
166724
+ });
166725
+ value.queues.clear();
166726
+ env4["AWS_ENDPOINT_URL_SQS"] = `http://127.0.0.1:${value.port}`;
166727
+ servers.push({
166728
+ name: "sqs",
166729
+ start({ dispatch, reportFailure }) {
166730
+ value.server.connect(dispatch, reportFailure);
166731
+ }
166732
+ });
166733
+ return { port: value.port, queues: value.queues };
166734
+ })();
166735
+ return sqs;
166736
+ };
166472
166737
  return {
166473
166738
  env: env4,
166474
166739
  routes,
@@ -166492,6 +166757,7 @@ var createDevContext = (props) => {
166492
166757
  peek: props.pool.peek,
166493
166758
  useDynamo,
166494
166759
  useStore,
166760
+ useSqs,
166495
166761
  addEnv(name, value) {
166496
166762
  if (name in env4 && env4[name] !== value) {
166497
166763
  throw new Error(`The env var "${name}" is defined multiple times with different values, while all bundled functions share the same env.`);
@@ -166517,10 +166783,10 @@ var createDevContext = (props) => {
166517
166783
  };
166518
166784
 
166519
166785
  // src/dev/seed.ts
166520
- import { spawn as spawn5 } from "child_process";
166521
- import { isAbsolute as isAbsolute4, join as join50 } from "path";
166786
+ import { spawn as spawn6 } from "child_process";
166787
+ import { isAbsolute as isAbsolute4, join as join51 } from "path";
166522
166788
  var createSeedRunner = (props) => {
166523
- const file2 = props.seed && (isAbsolute4(props.seed) ? props.seed : join50(directories.root, props.seed));
166789
+ const file2 = props.seed && (isAbsolute4(props.seed) ? props.seed : join51(directories.root, props.seed));
166524
166790
  let running;
166525
166791
  const run = () => {
166526
166792
  running ??= (async () => {
@@ -166530,7 +166796,7 @@ var createSeedRunner = (props) => {
166530
166796
  }
166531
166797
  debug(`Seeding from ${props.seed}`);
166532
166798
  await new Promise((resolve5, reject) => {
166533
- const child = spawn5("bun", [file2], {
166799
+ const child = spawn6("bun", [file2], {
166534
166800
  cwd: directories.root,
166535
166801
  stdio: ["ignore", "pipe", "pipe"],
166536
166802
  env: { ...process.env, ...props.env }
@@ -166571,13 +166837,13 @@ import { DynamoDBDocumentClient, ScanCommand as ScanCommand2 } from "@aws-sdk/li
166571
166837
  import { randomUUID as randomUUID6 } from "crypto";
166572
166838
  import { readdir as readdir9, readFile as readFile22, stat as stat8, writeFile as writeFile13 } from "fs/promises";
166573
166839
  import { createServer as createServer9 } from "http";
166574
- import { join as join52, relative as relative10, sep as sep5 } from "path";
166840
+ import { join as join53, relative as relative10, sep as sep5 } from "path";
166575
166841
 
166576
166842
  // src/dev/worker.ts
166577
- import { spawn as spawn6 } from "child_process";
166843
+ import { spawn as spawn7 } from "child_process";
166578
166844
  import { writeFile as writeFile12 } from "fs/promises";
166579
166845
  import { availableParallelism } from "os";
166580
- import { join as join51 } from "path";
166846
+ import { join as join52 } from "path";
166581
166847
  class WorkerError extends Error {
166582
166848
  name;
166583
166849
  constructor(name, message4, stack) {
@@ -166694,9 +166960,9 @@ var createBundleWorker = (props) => {
166694
166960
  throw new Error("The bundle worker never became ready.");
166695
166961
  };
166696
166962
  const startWorker = async () => {
166697
- const entry = join51(props.buildDir, "worker.mjs");
166963
+ const entry = join52(props.buildDir, "worker.mjs");
166698
166964
  const port = await findFreePort();
166699
- const child = spawn6("node", ["--enable-source-maps", entry], {
166965
+ const child = spawn7("node", ["--enable-source-maps", entry], {
166700
166966
  cwd: props.buildDir,
166701
166967
  stdio: ["ignore", "pipe", "pipe"],
166702
166968
  env: {
@@ -166748,7 +167014,7 @@ var createBundleWorker = (props) => {
166748
167014
  return worker;
166749
167015
  };
166750
167016
  const start = async () => {
166751
- await writeFile12(join51(props.buildDir, "worker.mjs"), WORKER_ENTRY);
167017
+ await writeFile12(join52(props.buildDir, "worker.mjs"), WORKER_ENTRY);
166752
167018
  workers = await Promise.all(Array.from({ length: concurrency }, () => startWorker()));
166753
167019
  };
166754
167020
  const stop = async () => {
@@ -167089,6 +167355,7 @@ const GROUPS = [
167089
167355
  ['function', 'Functions'],
167090
167356
  ['cron', 'Crons'],
167091
167357
  ['task', 'Tasks'],
167358
+ ['instance', 'Instances'],
167092
167359
  ['queue', 'Queues'],
167093
167360
  ['topic', 'Topics'],
167094
167361
  ['subscriber', 'Subscribers'],
@@ -167115,6 +167382,7 @@ const ICONS = {
167115
167382
  function: svg('<polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/>'),
167116
167383
  cron: svg('<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>'),
167117
167384
  task: svg('<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>'),
167385
+ instance: svg('<line x1="22" y1="12" x2="2" y2="12"/><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/><line x1="6" y1="16" x2="6.01" y2="16"/><line x1="10" y1="16" x2="10.01" y2="16"/>'),
167118
167386
  queue: svg('<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>'),
167119
167387
  topic: svg('<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>'),
167120
167388
  subscriber: svg('<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/>'),
@@ -167274,6 +167542,74 @@ const invokePanel = (main, r) => {
167274
167542
  }
167275
167543
  }
167276
167544
 
167545
+ const instancePanel = (main, r) => {
167546
+ // A message into the instance's queue, like Instance.stack.name()
167547
+ // sends. The last payload survives reloads & panel switches.
167548
+ const storageKey = 'invoke-payload:' + r.kind + ':' + (r.stack ?? '-') + ':' + r.id
167549
+ const placeholder = '{}'
167550
+ const input = $('textarea', { value: localStorage.getItem(storageKey) ?? placeholder, spellcheck: false })
167551
+ const status = $('span', { className: 'status' })
167552
+ const result = $('pre', { className: 'result', hidden: true })
167553
+ const send = $('button', { className: 'primary', textContent: 'Send' })
167554
+ const reset = $('button', { className: 'secondary', textContent: 'Reset' })
167555
+ const restart = $('button', { className: 'secondary', textContent: 'Restart' })
167556
+
167557
+ input.oninput = () => localStorage.setItem(storageKey, input.value)
167558
+
167559
+ reset.onclick = () => {
167560
+ localStorage.removeItem(storageKey)
167561
+ input.value = placeholder
167562
+ }
167563
+
167564
+ restart.onclick = async () => {
167565
+ restart.disabled = true
167566
+ status.textContent = ''
167567
+ result.hidden = true
167568
+ try {
167569
+ await api('/api/instance/restart', {
167570
+ method: 'POST',
167571
+ body: JSON.stringify({ stack: r.stack, id: r.id }),
167572
+ })
167573
+ status.textContent = 'restarted'
167574
+ } catch (error) {
167575
+ result.className = 'result error'
167576
+ result.textContent = String(error.message ?? error)
167577
+ result.hidden = false
167578
+ }
167579
+ restart.disabled = false
167580
+ }
167581
+
167582
+ send.onclick = async () => {
167583
+ send.disabled = true
167584
+ status.textContent = ''
167585
+ result.hidden = true
167586
+ try {
167587
+ const payload = input.value.trim() ? JSON.parse(input.value) : {}
167588
+ await api('/api/instance/send', {
167589
+ method: 'POST',
167590
+ body: JSON.stringify({ stack: r.stack, id: r.id, payload }),
167591
+ })
167592
+ status.textContent = 'sent'
167593
+ } catch (error) {
167594
+ result.className = 'result error'
167595
+ result.textContent = String(error.message ?? error)
167596
+ result.hidden = false
167597
+ }
167598
+ send.disabled = false
167599
+ }
167600
+
167601
+ main.append(
167602
+ $('p', {}, $('a', { href: r.detail, target: '_blank', style: 'color: var(--accent)' }, r.detail)),
167603
+ input,
167604
+ $('div', { className: 'actions' }, [send, reset, restart, status]),
167605
+ result,
167606
+ )
167607
+
167608
+ if (r.channel) {
167609
+ cleanupPanel = attachLogFeed(main, r.channel)
167610
+ }
167611
+ }
167612
+
167277
167613
  const pubsubPanel = (main, r) => {
167278
167614
  const topic = $('input', { placeholder: 'topic', value: 'my-topic', autocomplete: 'off' })
167279
167615
  const event = $('input', { placeholder: 'event', value: 'my-event', autocomplete: 'off' })
@@ -167910,6 +168246,7 @@ const renderResource = main => {
167910
168246
  $('p', { className: 'detail' }, [r.kind, r.detail ? ' \xB7 ' + r.detail : '', r.routeKey ? ' \xB7 ' + r.routeKey : '']),
167911
168247
  )
167912
168248
 
168249
+ if (r.kind === 'instance') return instancePanel(main, r)
167913
168250
  if (r.kind === 'pubsub') return pubsubPanel(main, r)
167914
168251
  if (r.kind === 'rpc') return rpcPanel(main, r)
167915
168252
  if (r.kind === 'search') return searchPanel(main, r)
@@ -168215,6 +168552,44 @@ var createDashboardServer = (props) => {
168215
168552
  });
168216
168553
  return { status: 200, body: JSON.stringify({ result: result ?? null }) };
168217
168554
  }
168555
+ if (url.pathname === "/api/instance/send" && req.method === "POST") {
168556
+ const { stack, id, payload } = JSON.parse((await readBody3(req)).toString() || "{}");
168557
+ const resource = props.resources.find((r5) => r5.kind === "instance" && r5.stack === stack && r5.id === id);
168558
+ if (!resource?.queueUrl) {
168559
+ return { status: 400, body: JSON.stringify({ error: `Unknown instance: ${stack}/${id}` }) };
168560
+ }
168561
+ const queueName = resource.queueUrl.split("/").at(-1) ?? "";
168562
+ const res = await fetch(resource.queueUrl, {
168563
+ method: "POST",
168564
+ headers: {
168565
+ "content-type": "application/x-amz-json-1.0",
168566
+ "x-amz-target": "AmazonSQS.SendMessage"
168567
+ },
168568
+ body: JSON.stringify({
168569
+ QueueUrl: resource.queueUrl,
168570
+ MessageBody: JSON.stringify(payload ?? {}),
168571
+ MessageAttributes: {
168572
+ queue: { DataType: "String", StringValue: queueName },
168573
+ queueUrl: { DataType: "String", StringValue: resource.queueUrl },
168574
+ queueName: { DataType: "String", StringValue: queueName }
168575
+ }
168576
+ })
168577
+ });
168578
+ if (!res.ok) {
168579
+ const data = await res.json().catch(() => ({}));
168580
+ return { status: 400, body: JSON.stringify({ error: data.message ?? res.statusText }) };
168581
+ }
168582
+ return { status: 200, body: JSON.stringify({ result: "sent" }) };
168583
+ }
168584
+ if (url.pathname === "/api/instance/restart" && req.method === "POST") {
168585
+ const { stack, id } = JSON.parse((await readBody3(req)).toString() || "{}");
168586
+ const resource = props.resources.find((r5) => r5.kind === "instance" && r5.stack === stack && r5.id === id);
168587
+ if (!resource?.restart) {
168588
+ return { status: 400, body: JSON.stringify({ error: `Unknown instance: ${stack}/${id}` }) };
168589
+ }
168590
+ await resource.restart();
168591
+ return { status: 200, body: JSON.stringify({ result: "restarted" }) };
168592
+ }
168218
168593
  if (url.pathname === "/api/table") {
168219
168594
  const name = url.searchParams.get("name");
168220
168595
  try {
@@ -168278,7 +168653,7 @@ var createDashboardServer = (props) => {
168278
168653
  return;
168279
168654
  }
168280
168655
  for (const entry of entries2) {
168281
- const path6 = join52(dir, entry.name);
168656
+ const path6 = join53(dir, entry.name);
168282
168657
  if (entry.isDirectory()) {
168283
168658
  await walk2(path6);
168284
168659
  } else {
@@ -168581,28 +168956,28 @@ var createLambdaServer = (props) => {
168581
168956
  // src/dev/sdk.ts
168582
168957
  import { readdir as readdir10, readFile as readFile23, mkdir as mkdir9, rm as rm11, symlink } from "fs/promises";
168583
168958
  import { createRequire } from "module";
168584
- import { dirname as dirname22, join as join53 } from "path";
168959
+ import { dirname as dirname23, join as join54 } from "path";
168585
168960
  var linkSdkPackages = async (buildDir, onWarn) => {
168586
- const filesDir = join53(buildDir, "files");
168961
+ const filesDir = join54(buildDir, "files");
168587
168962
  const packages = new Set;
168588
168963
  for (const file2 of await readdir10(filesDir)) {
168589
168964
  if (!file2.endsWith(".mjs")) {
168590
168965
  continue;
168591
168966
  }
168592
- const code = await readFile23(join53(filesDir, file2), "utf8");
168967
+ const code = await readFile23(join54(filesDir, file2), "utf8");
168593
168968
  for (const match3 of code.matchAll(/(?:from\s*|import\()\s*["'](@aws-sdk\/[a-z0-9-]+|sharp)["']/g)) {
168594
168969
  packages.add(match3[1]);
168595
168970
  }
168596
168971
  }
168597
- const projectRequire = createRequire(join53(directories.root, "noop.js"));
168972
+ const projectRequire = createRequire(join54(directories.root, "noop.js"));
168598
168973
  const ownRequire = createRequire(import.meta.url);
168599
168974
  const resolveFromStore = async (name) => {
168600
- const store = join53(directories.root, "node_modules", ".pnpm");
168975
+ const store = join54(directories.root, "node_modules", ".pnpm");
168601
168976
  const prefix = name.replaceAll("/", "+") + "@";
168602
168977
  try {
168603
168978
  const entries2 = (await readdir10(store)).filter((entry) => entry.startsWith(prefix));
168604
168979
  const best = entries2.sort((a4, b6) => a4.slice(prefix.length).localeCompare(b6.slice(prefix.length), undefined, { numeric: true })).at(-1);
168605
- return best ? join53(store, best, "node_modules", name) : undefined;
168980
+ return best ? join54(store, best, "node_modules", name) : undefined;
168606
168981
  } catch (_4) {
168607
168982
  return;
168608
168983
  }
@@ -168615,14 +168990,14 @@ var linkSdkPackages = async (buildDir, onWarn) => {
168615
168990
  let source = await resolveFromStore(name);
168616
168991
  if (!source) {
168617
168992
  try {
168618
- source = dirname22(ownRequire.resolve(`${name}/package.json`));
168993
+ source = dirname23(ownRequire.resolve(`${name}/package.json`));
168619
168994
  } catch (_4) {
168620
168995
  onWarn?.(`The bundle imports "${name}", which is neither a project dependency nor shipped with the cli. Add it to your project dependencies.`);
168621
168996
  continue;
168622
168997
  }
168623
168998
  }
168624
- const target2 = join53(buildDir, "node_modules", name);
168625
- await mkdir9(dirname22(target2), { recursive: true });
168999
+ const target2 = join54(buildDir, "node_modules", name);
169000
+ await mkdir9(dirname23(target2), { recursive: true });
168626
169001
  await rm11(target2, { recursive: true, force: true });
168627
169002
  await symlink(source, target2, "dir");
168628
169003
  }
@@ -168645,7 +169020,7 @@ var startDev = async (props) => {
168645
169020
  dev: true
168646
169021
  });
168647
169022
  ready2();
168648
- await mkdir10(join54(directories.output, "local"), { recursive: true });
169023
+ await mkdir10(join55(directories.output, "local"), { recursive: true });
168649
169024
  const routerPorts = {};
168650
169025
  Object.keys(appConfig.router ?? {}).forEach((id, index) => {
168651
169026
  routerPorts[id] = props.port + 1 + index;
@@ -168700,7 +169075,7 @@ var startDev = async (props) => {
168700
169075
  for (const [id, port] of Object.entries(routerPorts)) {
168701
169076
  env5[`ROUTER_${constantCase(id)}_ENDPOINT`] = `localhost:${port}`;
168702
169077
  }
168703
- await writeFile14(join54(directories.output, "local", "env.json"), JSON.stringify(env5, null, "\t") + `
169078
+ await writeFile14(join55(directories.output, "local", "env.json"), JSON.stringify(env5, null, "\t") + `
168704
169079
  `);
168705
169080
  return { env: env5, lambda: lambda2 };
168706
169081
  });
@@ -168835,8 +169210,8 @@ var startDev = async (props) => {
168835
169210
  resources: dev.resources,
168836
169211
  routes: dev.routes,
168837
169212
  env: env4,
168838
- storeRoot: join54(directories.output, "local", "store"),
168839
- configFile: join54(directories.output, "local", "config.json"),
169213
+ storeRoot: join55(directories.output, "local", "store"),
169214
+ configFile: join55(directories.output, "local", "config.json"),
168840
169215
  getEmails: () => props.pool.peek("shim:ses-email")?.server.list() ?? [],
168841
169216
  configPulled: Object.keys(props.pool.peek("config:pull") ?? {}),
168842
169217
  auth: createAuthAdmin({
@@ -168871,7 +169246,7 @@ var startDev = async (props) => {
168871
169246
  dashboardPort,
168872
169247
  routerPorts,
168873
169248
  async stop() {
168874
- await rm12(join54(directories.output, "local", "env.json"), { force: true });
169249
+ await rm12(join55(directories.output, "local", "env.json"), { force: true });
168875
169250
  clearTimeout(rebuildTimer);
168876
169251
  await watcher.close();
168877
169252
  await restartWatcher?.close();
@@ -168936,7 +169311,7 @@ var createServerPool = () => {
168936
169311
 
168937
169312
  // src/type-gen/generate.ts
168938
169313
  import { mkdir as mkdir11, writeFile as writeFile15 } from "fs/promises";
168939
- import { dirname as dirname23, join as join55, relative as relative11 } from "path";
169314
+ import { dirname as dirname24, join as join56, relative as relative11 } from "path";
168940
169315
  var generateTypes = async (props) => {
168941
169316
  const files = [];
168942
169317
  await Promise.all(features.map((feature) => {
@@ -168944,12 +169319,12 @@ var generateTypes = async (props) => {
168944
169319
  ...props,
168945
169320
  async write(file2, data, include = false) {
168946
169321
  const code = data?.toString("utf8");
168947
- const path6 = join55(directories.types, file2);
169322
+ const path6 = join56(directories.types, file2);
168948
169323
  if (code) {
168949
169324
  if (include) {
168950
169325
  files.push(relative11(directories.root, path6));
168951
169326
  }
168952
- await mkdir11(dirname23(path6), { recursive: true });
169327
+ await mkdir11(dirname24(path6), { recursive: true });
168953
169328
  await writeFile15(path6, code);
168954
169329
  }
168955
169330
  }
@@ -168958,7 +169333,7 @@ var generateTypes = async (props) => {
168958
169333
  if (files.length) {
168959
169334
  const code = files.map((file2) => `/// <reference path='${file2}' />`).join(`
168960
169335
  `);
168961
- await writeFile15(join55(directories.root, `awsless.d.ts`), code);
169336
+ await writeFile15(join56(directories.root, `awsless.d.ts`), code);
168962
169337
  }
168963
169338
  };
168964
169339
 
@@ -184588,12 +184963,12 @@ var state = (program3) => {
184588
184963
 
184589
184964
  // src/cli/command/test.ts
184590
184965
  import { mkdir as mkdir12, writeFile as writeFile16 } from "fs/promises";
184591
- import { join as join57 } from "path";
184966
+ import { join as join58 } from "path";
184592
184967
 
184593
184968
  // src/test/manifest.ts
184594
- import { isAbsolute as isAbsolute5, join as join56 } from "path";
184969
+ import { isAbsolute as isAbsolute5, join as join57 } from "path";
184595
184970
  var absolute = (file2) => {
184596
- return isAbsolute5(file2) ? file2 : join56(directories.root, file2);
184971
+ return isAbsolute5(file2) ? file2 : join57(directories.root, file2);
184597
184972
  };
184598
184973
  var createTestManifest = (appConfig, stackConfigs) => {
184599
184974
  const manifest = {
@@ -184689,7 +185064,7 @@ var test2 = (program3) => {
184689
185064
  return "No tests found.";
184690
185065
  }
184691
185066
  const manifest = createTestManifest(props.appConfig, props.stackConfigs);
184692
- const manifestFile = join57(directories.output, "test", "manifest.json");
185067
+ const manifestFile = join58(directories.output, "test", "manifest.json");
184693
185068
  let redis;
184694
185069
  let killSearch;
184695
185070
  manifest.servers = {};
@@ -184713,7 +185088,7 @@ var test2 = (program3) => {
184713
185088
  await redis.ping();
184714
185089
  manifest.servers.redis = { host: "127.0.0.1", port: await redis.getPort() };
184715
185090
  }
184716
- await mkdir12(join57(directories.output, "test"), { recursive: true });
185091
+ await mkdir12(join58(directories.output, "test"), { recursive: true });
184717
185092
  await writeFile16(manifestFile, JSON.stringify(manifest));
184718
185093
  process.env.AWSLESS_TEST_MANIFEST = manifestFile;
184719
185094
  let passed = false;