@awsless/cli 0.0.46-local.45 → 0.0.46-local.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -152672,9 +152672,18 @@ class TypeObject {
152672
152672
 
152673
152673
  // src/feature/auth/dev.ts
152674
152674
  import {
152675
+ AdminAddUserToGroupCommand,
152676
+ AdminCreateUserCommand,
152677
+ AdminGetUserCommand,
152678
+ AdminListGroupsForUserCommand,
152679
+ AdminRemoveUserFromGroupCommand,
152680
+ AdminSetUserPasswordCommand,
152675
152681
  CognitoIdentityProviderClient,
152676
152682
  ListUserPoolClientsCommand,
152677
- ListUserPoolsCommand
152683
+ ListUserPoolsCommand,
152684
+ ListUsersCommand,
152685
+ UserNotFoundException,
152686
+ UsernameExistsException
152678
152687
  } from "@aws-sdk/client-cognito-identity-provider";
152679
152688
  var authOnDev = async (ctx) => {
152680
152689
  const ids = Object.keys(ctx.appConfig.auth ?? {});
@@ -152745,13 +152754,199 @@ var authOnDev = async (ctx) => {
152745
152754
  for (const [id, pool] of Object.entries(pools)) {
152746
152755
  ctx.addEnv(`AUTH_${constantCase(id)}_USER_POOL_ID`, pool.userPoolId);
152747
152756
  ctx.addEnv(`AUTH_${constantCase(id)}_CLIENT_ID`, pool.clientId);
152757
+ }
152758
+ for (const id of ids) {
152748
152759
  ctx.registerResource({
152749
152760
  kind: "auth",
152750
152761
  id,
152751
- detail: pool.userPoolId
152762
+ detail: pools[id]?.userPoolId ?? "not deployed"
152752
152763
  });
152753
152764
  }
152754
152765
  };
152766
+ var createAuthAdmin = (props) => {
152767
+ let client;
152768
+ const getClient = async () => {
152769
+ client ??= new CognitoIdentityProviderClient({
152770
+ region: props.appConfig.region,
152771
+ credentials: await getCredentials(props.appConfig.profile)
152772
+ });
152773
+ return client;
152774
+ };
152775
+ const getPool = (id) => {
152776
+ const authProps = props.appConfig.auth?.[id];
152777
+ const resolved = props.resolvedPools()?.[id];
152778
+ if (!authProps) {
152779
+ throw new Error(`The auth userpool "${id}" doesn't exist.`);
152780
+ }
152781
+ if (!resolved) {
152782
+ throw new Error(`The auth userpool "${id}" isn't deployed yet.`);
152783
+ }
152784
+ return { ...authProps, userPoolId: resolved.userPoolId };
152785
+ };
152786
+ const validatePassword = (pool, value) => {
152787
+ if (!value) {
152788
+ return "A password is required";
152789
+ }
152790
+ if (value.length < pool.password.minLength) {
152791
+ return `The password min length is ${pool.password.minLength}`;
152792
+ }
152793
+ if (pool.password.lowercase && value.toUpperCase() === value) {
152794
+ return `The password should include lowercase characters`;
152795
+ }
152796
+ if (pool.password.uppercase && value.toLowerCase() === value) {
152797
+ return `The password should include uppercase characters`;
152798
+ }
152799
+ if (pool.password.numbers && !/\d/.test(value)) {
152800
+ return `The password should include numbers`;
152801
+ }
152802
+ if (pool.password.symbols && !/[ `!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/.test(value)) {
152803
+ return `The password should include symbols`;
152804
+ }
152805
+ return;
152806
+ };
152807
+ const validateGroups = (pool, groups) => {
152808
+ for (const group4 of groups) {
152809
+ if (!pool.groups.includes(group4)) {
152810
+ throw new Error(`The group "${group4}" doesn't exist.`);
152811
+ }
152812
+ }
152813
+ };
152814
+ const listUserGroups = async (userPoolId, username) => {
152815
+ const client2 = await getClient();
152816
+ const groups = [];
152817
+ let token;
152818
+ do {
152819
+ const result = await client2.send(new AdminListGroupsForUserCommand({
152820
+ UserPoolId: userPoolId,
152821
+ Username: username,
152822
+ NextToken: token
152823
+ }));
152824
+ groups.push(...result.Groups?.map((group4) => group4.GroupName) ?? []);
152825
+ token = result.NextToken;
152826
+ } while (token);
152827
+ return groups;
152828
+ };
152829
+ return {
152830
+ describePool(id) {
152831
+ const pool = getPool(id);
152832
+ return {
152833
+ groups: pool.groups
152834
+ };
152835
+ },
152836
+ async listUsers(id) {
152837
+ const pool = getPool(id);
152838
+ const client2 = await getClient();
152839
+ const users = [];
152840
+ let token;
152841
+ do {
152842
+ const result = await client2.send(new ListUsersCommand({
152843
+ UserPoolId: pool.userPoolId,
152844
+ PaginationToken: token
152845
+ }));
152846
+ for (const user of result.Users ?? []) {
152847
+ users.push({
152848
+ username: user.Username,
152849
+ email: user.Attributes?.find((a3) => a3.Name === "email")?.Value,
152850
+ status: user.UserStatus,
152851
+ enabled: user.Enabled ?? true,
152852
+ createdAt: user.UserCreateDate?.toISOString(),
152853
+ groups: []
152854
+ });
152855
+ }
152856
+ token = result.PaginationToken;
152857
+ } while (token && users.length < 500);
152858
+ await Promise.all(users.map(async (user) => {
152859
+ user.groups = await listUserGroups(pool.userPoolId, user.username);
152860
+ }));
152861
+ return users.sort((a3, b5) => a3.username.localeCompare(b5.username));
152862
+ },
152863
+ async createUser(id, input) {
152864
+ const pool = getPool(id);
152865
+ if (!input.username) {
152866
+ throw new Error("A username is required");
152867
+ }
152868
+ const issue = validatePassword(pool, input.password);
152869
+ if (issue) {
152870
+ throw new Error(issue);
152871
+ }
152872
+ validateGroups(pool, input.groups);
152873
+ const client2 = await getClient();
152874
+ try {
152875
+ await client2.send(new AdminCreateUserCommand({
152876
+ UserPoolId: pool.userPoolId,
152877
+ Username: input.username,
152878
+ TemporaryPassword: input.password
152879
+ }));
152880
+ } catch (error3) {
152881
+ if (error3 instanceof UsernameExistsException) {
152882
+ throw new Error("The user already exists");
152883
+ }
152884
+ throw error3;
152885
+ }
152886
+ await client2.send(new AdminSetUserPasswordCommand({
152887
+ UserPoolId: pool.userPoolId,
152888
+ Username: input.username,
152889
+ Password: input.password,
152890
+ Permanent: true
152891
+ }));
152892
+ for (const group4 of input.groups) {
152893
+ await client2.send(new AdminAddUserToGroupCommand({
152894
+ UserPoolId: pool.userPoolId,
152895
+ Username: input.username,
152896
+ GroupName: group4
152897
+ }));
152898
+ }
152899
+ },
152900
+ async updateUser(id, input) {
152901
+ const pool = getPool(id);
152902
+ validateGroups(pool, input.groups);
152903
+ if (input.password) {
152904
+ const issue = validatePassword(pool, input.password);
152905
+ if (issue) {
152906
+ throw new Error(issue);
152907
+ }
152908
+ }
152909
+ const client2 = await getClient();
152910
+ let oldGroups;
152911
+ try {
152912
+ await client2.send(new AdminGetUserCommand({
152913
+ UserPoolId: pool.userPoolId,
152914
+ Username: input.username
152915
+ }));
152916
+ oldGroups = await listUserGroups(pool.userPoolId, input.username);
152917
+ } catch (error3) {
152918
+ if (error3 instanceof UserNotFoundException) {
152919
+ throw new Error("The user does not exist");
152920
+ }
152921
+ throw error3;
152922
+ }
152923
+ if (input.password) {
152924
+ await client2.send(new AdminSetUserPasswordCommand({
152925
+ UserPoolId: pool.userPoolId,
152926
+ Username: input.username,
152927
+ Password: input.password,
152928
+ Permanent: true
152929
+ }));
152930
+ }
152931
+ const removed = oldGroups.filter((group4) => !input.groups.includes(group4));
152932
+ const added = input.groups.filter((group4) => !oldGroups.includes(group4));
152933
+ for (const group4 of removed) {
152934
+ await client2.send(new AdminRemoveUserFromGroupCommand({
152935
+ UserPoolId: pool.userPoolId,
152936
+ Username: input.username,
152937
+ GroupName: group4
152938
+ }));
152939
+ }
152940
+ for (const group4 of added) {
152941
+ await client2.send(new AdminAddUserToGroupCommand({
152942
+ UserPoolId: pool.userPoolId,
152943
+ Username: input.username,
152944
+ GroupName: group4
152945
+ }));
152946
+ }
152947
+ }
152948
+ };
152949
+ };
152755
152950
 
152756
152951
  // src/feature/auth/index.ts
152757
152952
  import { toDays as toDays4, toHours } from "@awsless/duration";
@@ -154827,6 +155022,9 @@ var decodeAwsChunked = (body) => {
154827
155022
  }
154828
155023
  return Buffer.concat(chunks);
154829
155024
  };
155025
+ var stripAnsi2 = (line) => {
155026
+ return line.replaceAll(/\x1b\[[0-9;?]*[a-zA-Z]/g, "");
155027
+ };
154830
155028
 
154831
155029
  // src/dev/servers/ssm.ts
154832
155030
  var createSsmServer = (props) => {
@@ -158065,7 +158263,20 @@ var startDevRouter = async (props) => {
158065
158263
  }
158066
158264
  const body = Buffer.from(await request.arrayBuffer());
158067
158265
  const sourceIp = server2.requestIP(request)?.address ?? "127.0.0.1";
158068
- const result = await props.dispatch(formatWebEvent(request, route, body, url, sourceIp));
158266
+ let result;
158267
+ try {
158268
+ result = await props.dispatch(formatWebEvent(request, route, body, url, sourceIp));
158269
+ } catch (error3) {
158270
+ props.onError?.(error3, route.routeKey);
158271
+ const detail = error3 instanceof Error ? error3.stack ?? error3.message : String(error3);
158272
+ return new Response(`500: Internal Error
158273
+
158274
+ ${route.routeKey}
158275
+ ${detail}`, {
158276
+ status: 500,
158277
+ headers: { "content-type": "text/plain" }
158278
+ });
158279
+ }
158069
158280
  return toResponse(result);
158070
158281
  },
158071
158282
  websocket: {
@@ -158850,7 +159061,7 @@ var planStaticRoutes = (files, sitePath) => {
158850
159061
  };
158851
159062
 
158852
159063
  // src/feature/site/dev.ts
158853
- var stripAnsi2 = (line) => {
159064
+ var stripAnsi3 = (line) => {
158854
159065
  return line.replaceAll(/\x1b\[[0-9;?]*[a-zA-Z]/g, "");
158855
159066
  };
158856
159067
  var binPath = (from) => {
@@ -158903,7 +159114,7 @@ var siteOnDev = async (ctx) => {
158903
159114
  const capture = (chunk3) => {
158904
159115
  for (const raw of chunk3.toString().split(`
158905
159116
  `)) {
158906
- const line = stripAnsi2(raw);
159117
+ const line = stripAnsi3(raw);
158907
159118
  if (line.trim() === "") {
158908
159119
  continue;
158909
159120
  }
@@ -162707,7 +162918,7 @@ var routerFeature = defineFeature({
162707
162918
  for (const [id, patterns] of Object.entries(stackConfig.routes ?? {})) {
162708
162919
  for (const pattern of Object.keys(patterns)) {
162709
162920
  const slug = kebabCase(pattern).slice(0, 20);
162710
- const routeKey = formatRouteKey(stackConfig.name, "route", `${slug || "root"}-${shortId(`${id}:${pattern}`)}`);
162921
+ const routeKey = formatRouteKey(stackConfig.name, "route", `${slug || "root"}-${shortId(pattern)}`);
162711
162922
  ctx.addRoute({
162712
162923
  routerId: id,
162713
162924
  pattern,
@@ -164666,11 +164877,11 @@ var deploy = (program3) => {
164666
164877
 
164667
164878
  // src/cli/command/auth/user/create.ts
164668
164879
  import {
164669
- AdminAddUserToGroupCommand,
164670
- AdminCreateUserCommand,
164671
- AdminSetUserPasswordCommand,
164880
+ AdminAddUserToGroupCommand as AdminAddUserToGroupCommand2,
164881
+ AdminCreateUserCommand as AdminCreateUserCommand2,
164882
+ AdminSetUserPasswordCommand as AdminSetUserPasswordCommand2,
164672
164883
  CognitoIdentityProviderClient as CognitoIdentityProviderClient2,
164673
- UsernameExistsException
164884
+ UsernameExistsException as UsernameExistsException2
164674
164885
  } from "@aws-sdk/client-cognito-identity-provider";
164675
164886
  var create = (program3) => {
164676
164887
  program3.command("create").description("Create an user in your userpool").option("--pool <name>", "The auth userpool name").option("--username <username>", "The username for the new user").option("--password <password>", "The password for the new user").option("--groups <groups...>", "The groups to add the new user to").action(async (options) => {
@@ -164818,18 +165029,18 @@ var create = (program3) => {
164818
165029
  errorMessage: "Failed creating user.",
164819
165030
  async task() {
164820
165031
  try {
164821
- await client.send(new AdminCreateUserCommand({
165032
+ await client.send(new AdminCreateUserCommand2({
164822
165033
  UserPoolId: userPoolId,
164823
165034
  Username: username,
164824
165035
  TemporaryPassword: password2
164825
165036
  }));
164826
165037
  } catch (error3) {
164827
- if (error3 instanceof UsernameExistsException) {
165038
+ if (error3 instanceof UsernameExistsException2) {
164828
165039
  throw new ExpectedError("User already exists");
164829
165040
  }
164830
165041
  throw error3;
164831
165042
  }
164832
- await client.send(new AdminSetUserPasswordCommand({
165043
+ await client.send(new AdminSetUserPasswordCommand2({
164833
165044
  UserPoolId: userPoolId,
164834
165045
  Username: username,
164835
165046
  Password: password2,
@@ -164837,7 +165048,7 @@ var create = (program3) => {
164837
165048
  }));
164838
165049
  if (groups.length > 0) {
164839
165050
  for (const group4 of groups) {
164840
- await client.send(new AdminAddUserToGroupCommand({
165051
+ await client.send(new AdminAddUserToGroupCommand2({
164841
165052
  UserPoolId: userPoolId,
164842
165053
  Username: username,
164843
165054
  GroupName: group4
@@ -164852,13 +165063,13 @@ var create = (program3) => {
164852
165063
 
164853
165064
  // src/cli/command/auth/user/update.ts
164854
165065
  import {
164855
- AdminAddUserToGroupCommand as AdminAddUserToGroupCommand2,
164856
- AdminGetUserCommand,
164857
- AdminListGroupsForUserCommand,
164858
- AdminRemoveUserFromGroupCommand,
164859
- AdminSetUserPasswordCommand as AdminSetUserPasswordCommand2,
165066
+ AdminAddUserToGroupCommand as AdminAddUserToGroupCommand3,
165067
+ AdminGetUserCommand as AdminGetUserCommand2,
165068
+ AdminListGroupsForUserCommand as AdminListGroupsForUserCommand2,
165069
+ AdminRemoveUserFromGroupCommand as AdminRemoveUserFromGroupCommand2,
165070
+ AdminSetUserPasswordCommand as AdminSetUserPasswordCommand3,
164860
165071
  CognitoIdentityProviderClient as CognitoIdentityProviderClient3,
164861
- UserNotFoundException
165072
+ UserNotFoundException as UserNotFoundException2
164862
165073
  } from "@aws-sdk/client-cognito-identity-provider";
164863
165074
  var update = (program3) => {
164864
165075
  program3.command("update").description("Update an user in your userpool").option("--pool <name>", "The auth userpool name").option("--username <username>", "The username of the user").option("--password <password>", "The new password for the user").option("--groups <groups...>", "The groups the user should be in, replacing the current groups").action(async (options) => {
@@ -164955,12 +165166,12 @@ var update = (program3) => {
164955
165166
  errorMessage: `Failed fetching user info.`,
164956
165167
  async task() {
164957
165168
  try {
164958
- await client.send(new AdminGetUserCommand({
165169
+ await client.send(new AdminGetUserCommand2({
164959
165170
  UserPoolId: userPoolId,
164960
165171
  Username: username
164961
165172
  }));
164962
165173
  } catch (error3) {
164963
- if (error3 instanceof UserNotFoundException) {
165174
+ if (error3 instanceof UserNotFoundException2) {
164964
165175
  throw new ExpectedError("User does not exist");
164965
165176
  }
164966
165177
  throw error3;
@@ -164968,7 +165179,7 @@ var update = (program3) => {
164968
165179
  const groups = [];
164969
165180
  let token;
164970
165181
  do {
164971
- const result = await client.send(new AdminListGroupsForUserCommand({
165182
+ const result = await client.send(new AdminListGroupsForUserCommand2({
164972
165183
  UserPoolId: userPoolId,
164973
165184
  Username: username,
164974
165185
  NextToken: token
@@ -165042,7 +165253,7 @@ var update = (program3) => {
165042
165253
  errorMessage: "Failed updating user.",
165043
165254
  async task() {
165044
165255
  if (password2) {
165045
- await client.send(new AdminSetUserPasswordCommand2({
165256
+ await client.send(new AdminSetUserPasswordCommand3({
165046
165257
  UserPoolId: userPoolId,
165047
165258
  Username: username,
165048
165259
  Password: password2,
@@ -165054,14 +165265,14 @@ var update = (program3) => {
165054
165265
  const removedGroups = oldGroupSet.difference(newGroupSet);
165055
165266
  const addedGroups = newGroupSet.difference(oldGroupSet);
165056
165267
  for (const group4 of removedGroups) {
165057
- await client.send(new AdminRemoveUserFromGroupCommand({
165268
+ await client.send(new AdminRemoveUserFromGroupCommand2({
165058
165269
  UserPoolId: userPoolId,
165059
165270
  Username: username,
165060
165271
  GroupName: group4
165061
165272
  }));
165062
165273
  }
165063
165274
  for (const group4 of addedGroups) {
165064
- await client.send(new AdminAddUserToGroupCommand2({
165275
+ await client.send(new AdminAddUserToGroupCommand3({
165065
165276
  UserPoolId: userPoolId,
165066
165277
  Username: username,
165067
165278
  GroupName: group4
@@ -165077,7 +165288,7 @@ var update = (program3) => {
165077
165288
  import {
165078
165289
  AdminDeleteUserCommand,
165079
165290
  CognitoIdentityProviderClient as CognitoIdentityProviderClient4,
165080
- UserNotFoundException as UserNotFoundException2
165291
+ UserNotFoundException as UserNotFoundException3
165081
165292
  } from "@aws-sdk/client-cognito-identity-provider";
165082
165293
  var del10 = (program3) => {
165083
165294
  program3.command("delete").description("Delete an user from your userpool").option("--pool <name>", "The auth userpool name").option("--username <username>", "The username of the user to delete").action(async (options) => {
@@ -165168,7 +165379,7 @@ var del10 = (program3) => {
165168
165379
  Username: username
165169
165380
  }));
165170
165381
  } catch (error3) {
165171
- if (error3 instanceof UserNotFoundException2) {
165382
+ if (error3 instanceof UserNotFoundException3) {
165172
165383
  throw new ExpectedError(`User doesn't exist`);
165173
165384
  }
165174
165385
  throw error3;
@@ -166503,8 +166714,18 @@ var createBundleWorker = (props) => {
166503
166714
  AWSLESS_DEV_WORKER_PORT: String(port)
166504
166715
  }
166505
166716
  });
166506
- child.stdout?.on("data", (chunk3) => process.stdout.write(chunk3));
166507
- child.stderr?.on("data", (chunk3) => process.stderr.write(chunk3));
166717
+ const capture = (stream2) => (chunk3) => {
166718
+ process[stream2].write(chunk3);
166719
+ for (const raw of chunk3.toString().split(`
166720
+ `)) {
166721
+ const line = stripAnsi2(raw);
166722
+ if (line.trim() !== "") {
166723
+ props.onOutput?.(line, stream2);
166724
+ }
166725
+ }
166726
+ };
166727
+ child.stdout?.on("data", capture("stdout"));
166728
+ child.stderr?.on("data", capture("stderr"));
166508
166729
  child.on("exit", (code) => {
166509
166730
  if (code !== null && code !== 0) {
166510
166731
  debug(`Bundle worker exited with code ${code}`);
@@ -166611,6 +166832,7 @@ var dashboardHtml = `<!doctype html>
166611
166832
  .logs .line { display: flex; gap: 8px; padding: 1px 0; }
166612
166833
  .logs .time { color: var(--muted); flex-shrink: 0; }
166613
166834
  .logs .text { white-space: pre-wrap; word-break: break-word; }
166835
+ .logs .line.error .text { color: var(--bad); }
166614
166836
  .config-form { display: flex; flex-direction: column; gap: 8px; max-width: 520px; }
166615
166837
  .config-form .field { display: grid; grid-template-columns: 180px 1fr; gap: 12px; align-items: center; }
166616
166838
  .config-form .name { color: var(--muted); overflow-wrap: anywhere; }
@@ -166623,6 +166845,8 @@ var dashboardHtml = `<!doctype html>
166623
166845
  font: inherit;
166624
166846
  }
166625
166847
  .config-form input::placeholder { color: var(--muted); }
166848
+ .groups { display: flex; flex-wrap: wrap; gap: 4px 16px; }
166849
+ .groups .group { display: flex; align-items: center; gap: 4px; cursor: pointer; }
166626
166850
  nav {
166627
166851
  border-right: 1px solid var(--border);
166628
166852
  overflow-y: auto;
@@ -166842,6 +167066,7 @@ const GROUPS = [
166842
167066
  ['config', 'Config'],
166843
167067
  ['auth', 'Auth'],
166844
167068
  ['email', 'Emails'],
167069
+ ['worker', 'Worker'],
166845
167070
  ['route', 'Routes'],
166846
167071
  ]
166847
167072
 
@@ -167127,7 +167352,7 @@ const attachLogFeed = (main, channel) => {
167127
167352
  const data = JSON.parse(message.data)
167128
167353
 
167129
167354
  feed.querySelector('.empty')?.remove()
167130
- feed.append($('div', { className: 'line' }, [
167355
+ feed.append($('div', { className: 'line' + (data.error ? ' error' : '') }, [
167131
167356
  $('span', { className: 'time' }, new Date(data.date).toLocaleTimeString()),
167132
167357
  $('span', { className: 'text' }, data.line),
167133
167358
  ]))
@@ -167457,6 +167682,138 @@ const configPanel = async (main) => {
167457
167682
  main.append(form, $('div', { className: 'actions' }, [save, status]))
167458
167683
  }
167459
167684
 
167685
+ // The users of a real deployed auth pool, with the same create &
167686
+ // group-update operations as the auth user cli commands.
167687
+ const authPanel = async (main, r) => {
167688
+ const holder = $('div', {}, $('p', { className: 'empty' }, 'Loading users...'))
167689
+ main.append(holder)
167690
+
167691
+ const groupBoxes = (groups, selected) => {
167692
+ const boxes = new Map()
167693
+ const row = $('div', { className: 'groups' })
167694
+
167695
+ for (const group of groups) {
167696
+ const box = $('input', { type: 'checkbox', checked: selected.includes(group) })
167697
+ boxes.set(group, box)
167698
+ row.append($('label', { className: 'group' }, [box, ' ' + group]))
167699
+ }
167700
+
167701
+ return { row, selected: () => [...boxes].filter(([, box]) => box.checked).map(([group]) => group) }
167702
+ }
167703
+
167704
+ try {
167705
+ const data = await api('/api/auth?pool=' + encodeURIComponent(r.id))
167706
+ holder.innerHTML = ''
167707
+
167708
+ // --------------------------------------------------------------
167709
+ // The user list, each row expanding into its group/password form.
167710
+
167711
+ if (data.users.length === 0) {
167712
+ holder.append($('p', { className: 'empty' }, 'The pool has no users.'))
167713
+ } else {
167714
+ const editRow = $('tr', { style: 'display: none' })
167715
+ const table = $('table', {}, [
167716
+ $('tr', {}, ['username', 'email', 'status', 'groups', 'created'].map(c => $('th', {}, c))),
167717
+ ])
167718
+
167719
+ for (const user of data.users) {
167720
+ const row = $('tr', {}, [
167721
+ $('td', {}, user.username),
167722
+ $('td', {}, user.email ?? ''),
167723
+ $('td', {}, user.enabled ? (user.status ?? '') : 'DISABLED'),
167724
+ $('td', {}, user.groups.join(', ')),
167725
+ $('td', {}, user.createdAt ? new Date(user.createdAt).toLocaleString() : ''),
167726
+ ])
167727
+
167728
+ row.style.cursor = 'pointer'
167729
+ row.onclick = () => {
167730
+ const groups = groupBoxes(data.groups, user.groups)
167731
+ const password = $('input', { type: 'password', placeholder: 'unchanged', spellcheck: false })
167732
+ const status = $('span', { className: 'status' })
167733
+ const save = $('button', { className: 'primary', textContent: 'Save' })
167734
+
167735
+ save.onclick = async () => {
167736
+ save.disabled = true
167737
+ try {
167738
+ await api('/api/auth/update', {
167739
+ method: 'POST',
167740
+ body: JSON.stringify({
167741
+ pool: r.id,
167742
+ username: user.username,
167743
+ password: password.value,
167744
+ groups: groups.selected(),
167745
+ }),
167746
+ })
167747
+ main.innerHTML = ''
167748
+ renderResource(main)
167749
+ return
167750
+ } catch (error) {
167751
+ status.textContent = String(error.message ?? error)
167752
+ }
167753
+ save.disabled = false
167754
+ }
167755
+
167756
+ editRow.innerHTML = ''
167757
+ editRow.style.display = ''
167758
+ editRow.append($('td', { colSpan: 5 }, $('div', { className: 'config-form' }, [
167759
+ $('label', { className: 'field' }, [$('span', { className: 'name' }, 'groups'), groups.row]),
167760
+ $('label', { className: 'field' }, [$('span', { className: 'name' }, 'new password'), password]),
167761
+ $('div', { className: 'actions' }, [save, status]),
167762
+ ])))
167763
+ row.after(editRow)
167764
+ }
167765
+
167766
+ table.append(row)
167767
+ }
167768
+
167769
+ holder.append(table)
167770
+ }
167771
+
167772
+ // --------------------------------------------------------------
167773
+ // Create a new user.
167774
+
167775
+ const username = $('input', { placeholder: 'username', spellcheck: false })
167776
+ const password = $('input', { type: 'password', placeholder: 'password', spellcheck: false })
167777
+ const groups = groupBoxes(data.groups, [])
167778
+ const status = $('span', { className: 'status' })
167779
+ const create = $('button', { className: 'primary', textContent: 'Create user' })
167780
+
167781
+ create.onclick = async () => {
167782
+ create.disabled = true
167783
+ try {
167784
+ await api('/api/auth/create', {
167785
+ method: 'POST',
167786
+ body: JSON.stringify({
167787
+ pool: r.id,
167788
+ username: username.value,
167789
+ password: password.value,
167790
+ groups: groups.selected(),
167791
+ }),
167792
+ })
167793
+ main.innerHTML = ''
167794
+ renderResource(main)
167795
+ return
167796
+ } catch (error) {
167797
+ status.textContent = String(error.message ?? error)
167798
+ }
167799
+ create.disabled = false
167800
+ }
167801
+
167802
+ holder.append(
167803
+ $('h3', {}, 'Create user'),
167804
+ $('div', { className: 'config-form' }, [
167805
+ $('label', { className: 'field' }, [$('span', { className: 'name' }, 'username'), username]),
167806
+ $('label', { className: 'field' }, [$('span', { className: 'name' }, 'password'), password]),
167807
+ $('label', { className: 'field' }, [$('span', { className: 'name' }, 'groups'), groups.row]),
167808
+ $('div', { className: 'actions' }, [create, status]),
167809
+ ]),
167810
+ )
167811
+ } catch (error) {
167812
+ holder.innerHTML = ''
167813
+ holder.append($('pre', { className: 'result error' }, String(error.message ?? error)))
167814
+ }
167815
+ }
167816
+
167460
167817
  const renderResource = main => {
167461
167818
  const r = view.resource
167462
167819
  const back = $('button', { className: 'back' }, '\u2190 ' + groupTitle(r.kind))
@@ -167480,6 +167837,11 @@ const renderResource = main => {
167480
167837
  if (r.kind === 'cache') return cachePanel(main, r)
167481
167838
  if (r.kind === 'store') return storePanel(main, r)
167482
167839
  if (r.kind === 'config') return configPanel(main)
167840
+ if (r.kind === 'auth') return authPanel(main, r)
167841
+ if (r.kind === 'worker') {
167842
+ cleanupPanel = attachLogFeed(main, 'worker')
167843
+ return
167844
+ }
167483
167845
  if (r.kind === 'route' || r.kind === 'site') {
167484
167846
  const url = r.url ?? r.detail
167485
167847
  main.append($('p', {}, $('a', { href: url, target: '_blank', style: 'color: var(--accent)' }, url)))
@@ -167513,6 +167875,13 @@ const renderList = main => {
167513
167875
  return emailPanel(main)
167514
167876
  }
167515
167877
 
167878
+ // The worker page streams the bundle worker output directly,
167879
+ // instead of listing its single resource.
167880
+ if (view.kind === 'worker') {
167881
+ cleanupPanel = attachLogFeed(main, 'worker')
167882
+ return
167883
+ }
167884
+
167516
167885
  const matches = r => {
167517
167886
  if (filter.stack && r.stack !== filter.stack) return false
167518
167887
  if (!filter.query) return true
@@ -167600,7 +167969,7 @@ const render = () => {
167600
167969
  const button = $('button', { className: view.kind === kind ? 'active' : '' }, [
167601
167970
  icon(kind),
167602
167971
  title,
167603
- kind === 'email' ? '' : $('span', { className: 'count' }, String(count)),
167972
+ kind === 'email' || kind === 'worker' ? '' : $('span', { className: 'count' }, String(count)),
167604
167973
  ])
167605
167974
  button.onclick = () => selectKind(kind)
167606
167975
  nav.append(button)
@@ -167861,6 +168230,36 @@ var createDashboardServer = (props) => {
167861
168230
  if (url.pathname === "/api/emails") {
167862
168231
  return { status: 200, body: JSON.stringify({ emails: props.getEmails?.() ?? [] }) };
167863
168232
  }
168233
+ if (url.pathname.startsWith("/api/auth") && props.auth) {
168234
+ const auth2 = props.auth;
168235
+ try {
168236
+ if (url.pathname === "/api/auth" && req.method === "GET") {
168237
+ const pool = url.searchParams.get("pool") ?? "";
168238
+ return {
168239
+ status: 200,
168240
+ body: JSON.stringify({
168241
+ ...auth2.describePool(pool),
168242
+ users: await auth2.listUsers(pool)
168243
+ })
168244
+ };
168245
+ }
168246
+ if (url.pathname === "/api/auth/create" && req.method === "POST") {
168247
+ const { pool, username, password: password2, groups } = JSON.parse((await readBody3(req)).toString() || "{}");
168248
+ await auth2.createUser(pool, { username, password: password2, groups: groups ?? [] });
168249
+ return { status: 200, body: JSON.stringify({ ok: true }) };
168250
+ }
168251
+ if (url.pathname === "/api/auth/update" && req.method === "POST") {
168252
+ const { pool, username, password: password2, groups } = JSON.parse((await readBody3(req)).toString() || "{}");
168253
+ await auth2.updateUser(pool, { username, password: password2 || undefined, groups: groups ?? [] });
168254
+ return { status: 200, body: JSON.stringify({ ok: true }) };
168255
+ }
168256
+ } catch (error3) {
168257
+ return {
168258
+ status: 400,
168259
+ body: JSON.stringify({ error: error3 instanceof Error ? error3.message : String(error3) })
168260
+ };
168261
+ }
168262
+ }
167864
168263
  if (url.pathname === "/api/config") {
167865
168264
  if (req.method === "PUT") {
167866
168265
  const values2 = JSON.parse((await readBody3(req)).toString() || "{}");
@@ -168245,7 +168644,21 @@ var startDev = async (props) => {
168245
168644
  workspace = await loadWorkspace3(directories.root);
168246
168645
  await buildAll();
168247
168646
  });
168248
- const worker = createBundleWorker({ buildDir, env: env4, functionName: bundleName });
168647
+ const emitWorkerLine = (line, error3 = false) => {
168648
+ dev.events.emit("worker", { date: Date.now(), line, error: error3 });
168649
+ };
168650
+ const worker = createBundleWorker({
168651
+ buildDir,
168652
+ env: env4,
168653
+ functionName: bundleName,
168654
+ onOutput: (line, stream2) => emitWorkerLine(line, stream2 === "stderr")
168655
+ });
168656
+ dev.resources.push({
168657
+ kind: "worker",
168658
+ id: "bundle",
168659
+ channel: "worker",
168660
+ detail: "The output & errors of the local bundle worker"
168661
+ });
168249
168662
  let dirty = false;
168250
168663
  let fresh;
168251
168664
  let restartNeeded = false;
@@ -168321,7 +168734,13 @@ var startDev = async (props) => {
168321
168734
  routers.push(await startDevRouter({
168322
168735
  routes: dev.routes.filter((route) => route.routerId === id),
168323
168736
  port,
168324
- dispatch
168737
+ dispatch,
168738
+ onError(error3, routeKey) {
168739
+ const detail = error3 instanceof Error ? error3.stack ?? error3.message : String(error3);
168740
+ process.stderr.write(`Route ${routeKey} failed: ${detail}
168741
+ `);
168742
+ emitWorkerLine(`Route ${routeKey} failed: ${detail}`, true);
168743
+ }
168325
168744
  }));
168326
168745
  }
168327
168746
  const dashboardPort = props.port;
@@ -168340,6 +168759,10 @@ var startDev = async (props) => {
168340
168759
  configFile: join54(directories.output, "local", "config.json"),
168341
168760
  getEmails: () => props.pool.peek("shim:ses-email")?.server.list() ?? [],
168342
168761
  configPulled: Object.keys(props.pool.peek("config:pull") ?? {}),
168762
+ auth: createAuthAdmin({
168763
+ appConfig,
168764
+ resolvedPools: () => props.pool.peek("auth:pull")
168765
+ }),
168343
168766
  events: dev.events
168344
168767
  });
168345
168768
  dashboard.connect(dispatch);
@@ -452,7 +452,9 @@ var createBundle = (handlers) => {
452
452
  }
453
453
  throw new Error("Unknown bundle route: " + match2.key);
454
454
  }
455
- console.trace(`Bundle route: ${match2.key}`);
455
+ if (process.env.AWSLESS_ENV !== "local") {
456
+ console.trace(`Bundle route: ${match2.key}`);
457
+ }
456
458
  process.env.AWSLESS_ROUTE = match2.key;
457
459
  return withBundleRouteContext(match2.key, internalInvoke, async () => {
458
460
  const handle = await load();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awsless/cli",
3
- "version": "0.0.46-local.45",
3
+ "version": "0.0.46-local.47",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -38,19 +38,19 @@
38
38
  "@opensearch-project/opensearch": "^2.13.0",
39
39
  "rolldown": "1.2.1",
40
40
  "vitest": "4.1.10",
41
- "@awsless/dynamodb-server": "^0.1.8",
42
- "@awsless/ts-file-cache": "^0.0.16"
41
+ "@awsless/ts-file-cache": "^0.0.16",
42
+ "@awsless/dynamodb-server": "^0.1.8"
43
43
  },
44
44
  "peerDependencies": {
45
+ "@awsless/dynamodb": "^0.3.22",
45
46
  "@awsless/big-float": "^0.1.7",
47
+ "@awsless/lambda": "^0.0.47",
48
+ "@awsless/validate": "^0.1.7",
46
49
  "@awsless/s3": "^0.0.22",
47
50
  "@awsless/duration": "^0.0.4",
48
- "@awsless/validate": "^0.1.7",
49
- "@awsless/lambda": "^0.0.47",
50
- "@awsless/dynamodb": "^0.3.22",
51
+ "@awsless/json": "^0.0.11",
51
52
  "@awsless/weak-cache": "^0.0.1",
52
- "awsless": "^0.0.15-local.11",
53
- "@awsless/json": "^0.0.11"
53
+ "awsless": "^0.0.15-local.11"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@aws-sdk/client-cloudformation": "^3.369.0",
@@ -121,24 +121,24 @@
121
121
  "zod": "^3.24.2",
122
122
  "zod-to-json-schema": "^3.24.3",
123
123
  "@awsless/big-float": "^0.1.7",
124
- "@awsless/cloudwatch": "^0.0.1",
125
124
  "@awsless/clui": "^0.0.9",
126
125
  "@awsless/duration": "^0.0.4",
126
+ "@awsless/cloudwatch": "^0.0.1",
127
+ "@awsless/iot": "^0.0.5",
127
128
  "@awsless/json": "^0.0.11",
129
+ "@awsless/dynamodb": "^0.3.22",
130
+ "@awsless/open-search": "^0.0.26",
128
131
  "@awsless/lambda": "^0.0.47",
132
+ "@awsless/s3": "^0.0.22",
129
133
  "@awsless/redis": "^0.1.13",
130
134
  "@awsless/size": "^0.0.2",
131
- "@awsless/iot": "^0.0.5",
132
- "@awsless/open-search": "^0.0.26",
133
- "@awsless/s3": "^0.0.22",
134
- "@awsless/dynamodb": "^0.3.22",
135
135
  "@awsless/sns": "^0.0.11",
136
136
  "@awsless/scheduler": "^0.0.5",
137
137
  "@awsless/sqs": "^0.0.24",
138
138
  "@awsless/validate": "^0.1.7",
139
+ "@awsless/ssm": "^0.0.8",
139
140
  "@awsless/weak-cache": "^0.0.1",
140
- "awsless": "^0.0.15-local.11",
141
- "@awsless/ssm": "^0.0.8"
141
+ "awsless": "^0.0.15-local.11"
142
142
  },
143
143
  "scripts": {
144
144
  "test": "bun cli/build-handlers.ts && pnpm vitest",