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

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,200 @@ 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
+ password: pool.password
152835
+ };
152836
+ },
152837
+ async listUsers(id) {
152838
+ const pool = getPool(id);
152839
+ const client2 = await getClient();
152840
+ const users = [];
152841
+ let token;
152842
+ do {
152843
+ const result = await client2.send(new ListUsersCommand({
152844
+ UserPoolId: pool.userPoolId,
152845
+ PaginationToken: token
152846
+ }));
152847
+ for (const user of result.Users ?? []) {
152848
+ users.push({
152849
+ username: user.Username,
152850
+ email: user.Attributes?.find((a3) => a3.Name === "email")?.Value,
152851
+ status: user.UserStatus,
152852
+ enabled: user.Enabled ?? true,
152853
+ createdAt: user.UserCreateDate?.toISOString(),
152854
+ groups: []
152855
+ });
152856
+ }
152857
+ token = result.PaginationToken;
152858
+ } while (token && users.length < 500);
152859
+ await Promise.all(users.map(async (user) => {
152860
+ user.groups = await listUserGroups(pool.userPoolId, user.username);
152861
+ }));
152862
+ return users.sort((a3, b5) => a3.username.localeCompare(b5.username));
152863
+ },
152864
+ async createUser(id, input) {
152865
+ const pool = getPool(id);
152866
+ if (!input.username) {
152867
+ throw new Error("A username is required");
152868
+ }
152869
+ const issue = validatePassword(pool, input.password);
152870
+ if (issue) {
152871
+ throw new Error(issue);
152872
+ }
152873
+ validateGroups(pool, input.groups);
152874
+ const client2 = await getClient();
152875
+ try {
152876
+ await client2.send(new AdminCreateUserCommand({
152877
+ UserPoolId: pool.userPoolId,
152878
+ Username: input.username,
152879
+ TemporaryPassword: input.password
152880
+ }));
152881
+ } catch (error3) {
152882
+ if (error3 instanceof UsernameExistsException) {
152883
+ throw new Error("The user already exists");
152884
+ }
152885
+ throw error3;
152886
+ }
152887
+ await client2.send(new AdminSetUserPasswordCommand({
152888
+ UserPoolId: pool.userPoolId,
152889
+ Username: input.username,
152890
+ Password: input.password,
152891
+ Permanent: true
152892
+ }));
152893
+ for (const group4 of input.groups) {
152894
+ await client2.send(new AdminAddUserToGroupCommand({
152895
+ UserPoolId: pool.userPoolId,
152896
+ Username: input.username,
152897
+ GroupName: group4
152898
+ }));
152899
+ }
152900
+ },
152901
+ async updateUser(id, input) {
152902
+ const pool = getPool(id);
152903
+ validateGroups(pool, input.groups);
152904
+ if (input.password) {
152905
+ const issue = validatePassword(pool, input.password);
152906
+ if (issue) {
152907
+ throw new Error(issue);
152908
+ }
152909
+ }
152910
+ const client2 = await getClient();
152911
+ let oldGroups;
152912
+ try {
152913
+ await client2.send(new AdminGetUserCommand({
152914
+ UserPoolId: pool.userPoolId,
152915
+ Username: input.username
152916
+ }));
152917
+ oldGroups = await listUserGroups(pool.userPoolId, input.username);
152918
+ } catch (error3) {
152919
+ if (error3 instanceof UserNotFoundException) {
152920
+ throw new Error("The user does not exist");
152921
+ }
152922
+ throw error3;
152923
+ }
152924
+ if (input.password) {
152925
+ await client2.send(new AdminSetUserPasswordCommand({
152926
+ UserPoolId: pool.userPoolId,
152927
+ Username: input.username,
152928
+ Password: input.password,
152929
+ Permanent: true
152930
+ }));
152931
+ }
152932
+ const removed = oldGroups.filter((group4) => !input.groups.includes(group4));
152933
+ const added = input.groups.filter((group4) => !oldGroups.includes(group4));
152934
+ for (const group4 of removed) {
152935
+ await client2.send(new AdminRemoveUserFromGroupCommand({
152936
+ UserPoolId: pool.userPoolId,
152937
+ Username: input.username,
152938
+ GroupName: group4
152939
+ }));
152940
+ }
152941
+ for (const group4 of added) {
152942
+ await client2.send(new AdminAddUserToGroupCommand({
152943
+ UserPoolId: pool.userPoolId,
152944
+ Username: input.username,
152945
+ GroupName: group4
152946
+ }));
152947
+ }
152948
+ }
152949
+ };
152950
+ };
152755
152951
 
152756
152952
  // src/feature/auth/index.ts
152757
152953
  import { toDays as toDays4, toHours } from "@awsless/duration";
@@ -154827,6 +155023,9 @@ var decodeAwsChunked = (body) => {
154827
155023
  }
154828
155024
  return Buffer.concat(chunks);
154829
155025
  };
155026
+ var stripAnsi2 = (line) => {
155027
+ return line.replaceAll(/\x1b\[[0-9;?]*[a-zA-Z]/g, "");
155028
+ };
154830
155029
 
154831
155030
  // src/dev/servers/ssm.ts
154832
155031
  var createSsmServer = (props) => {
@@ -158065,7 +158264,20 @@ var startDevRouter = async (props) => {
158065
158264
  }
158066
158265
  const body = Buffer.from(await request.arrayBuffer());
158067
158266
  const sourceIp = server2.requestIP(request)?.address ?? "127.0.0.1";
158068
- const result = await props.dispatch(formatWebEvent(request, route, body, url, sourceIp));
158267
+ let result;
158268
+ try {
158269
+ result = await props.dispatch(formatWebEvent(request, route, body, url, sourceIp));
158270
+ } catch (error3) {
158271
+ props.onError?.(error3, route.routeKey);
158272
+ const detail = error3 instanceof Error ? error3.stack ?? error3.message : String(error3);
158273
+ return new Response(`500: Internal Error
158274
+
158275
+ ${route.routeKey}
158276
+ ${detail}`, {
158277
+ status: 500,
158278
+ headers: { "content-type": "text/plain" }
158279
+ });
158280
+ }
158069
158281
  return toResponse(result);
158070
158282
  },
158071
158283
  websocket: {
@@ -158850,7 +159062,7 @@ var planStaticRoutes = (files, sitePath) => {
158850
159062
  };
158851
159063
 
158852
159064
  // src/feature/site/dev.ts
158853
- var stripAnsi2 = (line) => {
159065
+ var stripAnsi3 = (line) => {
158854
159066
  return line.replaceAll(/\x1b\[[0-9;?]*[a-zA-Z]/g, "");
158855
159067
  };
158856
159068
  var binPath = (from) => {
@@ -158903,7 +159115,7 @@ var siteOnDev = async (ctx) => {
158903
159115
  const capture = (chunk3) => {
158904
159116
  for (const raw of chunk3.toString().split(`
158905
159117
  `)) {
158906
- const line = stripAnsi2(raw);
159118
+ const line = stripAnsi3(raw);
158907
159119
  if (line.trim() === "") {
158908
159120
  continue;
158909
159121
  }
@@ -162707,7 +162919,7 @@ var routerFeature = defineFeature({
162707
162919
  for (const [id, patterns] of Object.entries(stackConfig.routes ?? {})) {
162708
162920
  for (const pattern of Object.keys(patterns)) {
162709
162921
  const slug = kebabCase(pattern).slice(0, 20);
162710
- const routeKey = formatRouteKey(stackConfig.name, "route", `${slug || "root"}-${shortId(`${id}:${pattern}`)}`);
162922
+ const routeKey = formatRouteKey(stackConfig.name, "route", `${slug || "root"}-${shortId(pattern)}`);
162711
162923
  ctx.addRoute({
162712
162924
  routerId: id,
162713
162925
  pattern,
@@ -164666,11 +164878,11 @@ var deploy = (program3) => {
164666
164878
 
164667
164879
  // src/cli/command/auth/user/create.ts
164668
164880
  import {
164669
- AdminAddUserToGroupCommand,
164670
- AdminCreateUserCommand,
164671
- AdminSetUserPasswordCommand,
164881
+ AdminAddUserToGroupCommand as AdminAddUserToGroupCommand2,
164882
+ AdminCreateUserCommand as AdminCreateUserCommand2,
164883
+ AdminSetUserPasswordCommand as AdminSetUserPasswordCommand2,
164672
164884
  CognitoIdentityProviderClient as CognitoIdentityProviderClient2,
164673
- UsernameExistsException
164885
+ UsernameExistsException as UsernameExistsException2
164674
164886
  } from "@aws-sdk/client-cognito-identity-provider";
164675
164887
  var create = (program3) => {
164676
164888
  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 +165030,18 @@ var create = (program3) => {
164818
165030
  errorMessage: "Failed creating user.",
164819
165031
  async task() {
164820
165032
  try {
164821
- await client.send(new AdminCreateUserCommand({
165033
+ await client.send(new AdminCreateUserCommand2({
164822
165034
  UserPoolId: userPoolId,
164823
165035
  Username: username,
164824
165036
  TemporaryPassword: password2
164825
165037
  }));
164826
165038
  } catch (error3) {
164827
- if (error3 instanceof UsernameExistsException) {
165039
+ if (error3 instanceof UsernameExistsException2) {
164828
165040
  throw new ExpectedError("User already exists");
164829
165041
  }
164830
165042
  throw error3;
164831
165043
  }
164832
- await client.send(new AdminSetUserPasswordCommand({
165044
+ await client.send(new AdminSetUserPasswordCommand2({
164833
165045
  UserPoolId: userPoolId,
164834
165046
  Username: username,
164835
165047
  Password: password2,
@@ -164837,7 +165049,7 @@ var create = (program3) => {
164837
165049
  }));
164838
165050
  if (groups.length > 0) {
164839
165051
  for (const group4 of groups) {
164840
- await client.send(new AdminAddUserToGroupCommand({
165052
+ await client.send(new AdminAddUserToGroupCommand2({
164841
165053
  UserPoolId: userPoolId,
164842
165054
  Username: username,
164843
165055
  GroupName: group4
@@ -164852,13 +165064,13 @@ var create = (program3) => {
164852
165064
 
164853
165065
  // src/cli/command/auth/user/update.ts
164854
165066
  import {
164855
- AdminAddUserToGroupCommand as AdminAddUserToGroupCommand2,
164856
- AdminGetUserCommand,
164857
- AdminListGroupsForUserCommand,
164858
- AdminRemoveUserFromGroupCommand,
164859
- AdminSetUserPasswordCommand as AdminSetUserPasswordCommand2,
165067
+ AdminAddUserToGroupCommand as AdminAddUserToGroupCommand3,
165068
+ AdminGetUserCommand as AdminGetUserCommand2,
165069
+ AdminListGroupsForUserCommand as AdminListGroupsForUserCommand2,
165070
+ AdminRemoveUserFromGroupCommand as AdminRemoveUserFromGroupCommand2,
165071
+ AdminSetUserPasswordCommand as AdminSetUserPasswordCommand3,
164860
165072
  CognitoIdentityProviderClient as CognitoIdentityProviderClient3,
164861
- UserNotFoundException
165073
+ UserNotFoundException as UserNotFoundException2
164862
165074
  } from "@aws-sdk/client-cognito-identity-provider";
164863
165075
  var update = (program3) => {
164864
165076
  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 +165167,12 @@ var update = (program3) => {
164955
165167
  errorMessage: `Failed fetching user info.`,
164956
165168
  async task() {
164957
165169
  try {
164958
- await client.send(new AdminGetUserCommand({
165170
+ await client.send(new AdminGetUserCommand2({
164959
165171
  UserPoolId: userPoolId,
164960
165172
  Username: username
164961
165173
  }));
164962
165174
  } catch (error3) {
164963
- if (error3 instanceof UserNotFoundException) {
165175
+ if (error3 instanceof UserNotFoundException2) {
164964
165176
  throw new ExpectedError("User does not exist");
164965
165177
  }
164966
165178
  throw error3;
@@ -164968,7 +165180,7 @@ var update = (program3) => {
164968
165180
  const groups = [];
164969
165181
  let token;
164970
165182
  do {
164971
- const result = await client.send(new AdminListGroupsForUserCommand({
165183
+ const result = await client.send(new AdminListGroupsForUserCommand2({
164972
165184
  UserPoolId: userPoolId,
164973
165185
  Username: username,
164974
165186
  NextToken: token
@@ -165042,7 +165254,7 @@ var update = (program3) => {
165042
165254
  errorMessage: "Failed updating user.",
165043
165255
  async task() {
165044
165256
  if (password2) {
165045
- await client.send(new AdminSetUserPasswordCommand2({
165257
+ await client.send(new AdminSetUserPasswordCommand3({
165046
165258
  UserPoolId: userPoolId,
165047
165259
  Username: username,
165048
165260
  Password: password2,
@@ -165054,14 +165266,14 @@ var update = (program3) => {
165054
165266
  const removedGroups = oldGroupSet.difference(newGroupSet);
165055
165267
  const addedGroups = newGroupSet.difference(oldGroupSet);
165056
165268
  for (const group4 of removedGroups) {
165057
- await client.send(new AdminRemoveUserFromGroupCommand({
165269
+ await client.send(new AdminRemoveUserFromGroupCommand2({
165058
165270
  UserPoolId: userPoolId,
165059
165271
  Username: username,
165060
165272
  GroupName: group4
165061
165273
  }));
165062
165274
  }
165063
165275
  for (const group4 of addedGroups) {
165064
- await client.send(new AdminAddUserToGroupCommand2({
165276
+ await client.send(new AdminAddUserToGroupCommand3({
165065
165277
  UserPoolId: userPoolId,
165066
165278
  Username: username,
165067
165279
  GroupName: group4
@@ -165077,7 +165289,7 @@ var update = (program3) => {
165077
165289
  import {
165078
165290
  AdminDeleteUserCommand,
165079
165291
  CognitoIdentityProviderClient as CognitoIdentityProviderClient4,
165080
- UserNotFoundException as UserNotFoundException2
165292
+ UserNotFoundException as UserNotFoundException3
165081
165293
  } from "@aws-sdk/client-cognito-identity-provider";
165082
165294
  var del10 = (program3) => {
165083
165295
  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 +165380,7 @@ var del10 = (program3) => {
165168
165380
  Username: username
165169
165381
  }));
165170
165382
  } catch (error3) {
165171
- if (error3 instanceof UserNotFoundException2) {
165383
+ if (error3 instanceof UserNotFoundException3) {
165172
165384
  throw new ExpectedError(`User doesn't exist`);
165173
165385
  }
165174
165386
  throw error3;
@@ -166503,8 +166715,18 @@ var createBundleWorker = (props) => {
166503
166715
  AWSLESS_DEV_WORKER_PORT: String(port)
166504
166716
  }
166505
166717
  });
166506
- child.stdout?.on("data", (chunk3) => process.stdout.write(chunk3));
166507
- child.stderr?.on("data", (chunk3) => process.stderr.write(chunk3));
166718
+ const capture = (stream2) => (chunk3) => {
166719
+ process[stream2].write(chunk3);
166720
+ for (const raw of chunk3.toString().split(`
166721
+ `)) {
166722
+ const line = stripAnsi2(raw);
166723
+ if (line.trim() !== "") {
166724
+ props.onOutput?.(line, stream2);
166725
+ }
166726
+ }
166727
+ };
166728
+ child.stdout?.on("data", capture("stdout"));
166729
+ child.stderr?.on("data", capture("stderr"));
166508
166730
  child.on("exit", (code) => {
166509
166731
  if (code !== null && code !== 0) {
166510
166732
  debug(`Bundle worker exited with code ${code}`);
@@ -166611,6 +166833,7 @@ var dashboardHtml = `<!doctype html>
166611
166833
  .logs .line { display: flex; gap: 8px; padding: 1px 0; }
166612
166834
  .logs .time { color: var(--muted); flex-shrink: 0; }
166613
166835
  .logs .text { white-space: pre-wrap; word-break: break-word; }
166836
+ .logs .line.error .text { color: var(--bad); }
166614
166837
  .config-form { display: flex; flex-direction: column; gap: 8px; max-width: 520px; }
166615
166838
  .config-form .field { display: grid; grid-template-columns: 180px 1fr; gap: 12px; align-items: center; }
166616
166839
  .config-form .name { color: var(--muted); overflow-wrap: anywhere; }
@@ -166623,6 +166846,8 @@ var dashboardHtml = `<!doctype html>
166623
166846
  font: inherit;
166624
166847
  }
166625
166848
  .config-form input::placeholder { color: var(--muted); }
166849
+ .groups { display: flex; flex-wrap: wrap; gap: 4px 16px; }
166850
+ .groups .group { display: flex; align-items: center; gap: 4px; cursor: pointer; }
166626
166851
  nav {
166627
166852
  border-right: 1px solid var(--border);
166628
166853
  overflow-y: auto;
@@ -166842,6 +167067,7 @@ const GROUPS = [
166842
167067
  ['config', 'Config'],
166843
167068
  ['auth', 'Auth'],
166844
167069
  ['email', 'Emails'],
167070
+ ['worker', 'Worker'],
166845
167071
  ['route', 'Routes'],
166846
167072
  ]
166847
167073
 
@@ -167127,7 +167353,7 @@ const attachLogFeed = (main, channel) => {
167127
167353
  const data = JSON.parse(message.data)
167128
167354
 
167129
167355
  feed.querySelector('.empty')?.remove()
167130
- feed.append($('div', { className: 'line' }, [
167356
+ feed.append($('div', { className: 'line' + (data.error ? ' error' : '') }, [
167131
167357
  $('span', { className: 'time' }, new Date(data.date).toLocaleTimeString()),
167132
167358
  $('span', { className: 'text' }, data.line),
167133
167359
  ]))
@@ -167457,6 +167683,138 @@ const configPanel = async (main) => {
167457
167683
  main.append(form, $('div', { className: 'actions' }, [save, status]))
167458
167684
  }
167459
167685
 
167686
+ // The users of a real deployed auth pool, with the same create &
167687
+ // group-update operations as the auth user cli commands.
167688
+ const authPanel = async (main, r) => {
167689
+ const holder = $('div', {}, $('p', { className: 'empty' }, 'Loading users...'))
167690
+ main.append(holder)
167691
+
167692
+ const groupBoxes = (groups, selected) => {
167693
+ const boxes = new Map()
167694
+ const row = $('div', { className: 'groups' })
167695
+
167696
+ for (const group of groups) {
167697
+ const box = $('input', { type: 'checkbox', checked: selected.includes(group) })
167698
+ boxes.set(group, box)
167699
+ row.append($('label', { className: 'group' }, [box, ' ' + group]))
167700
+ }
167701
+
167702
+ return { row, selected: () => [...boxes].filter(([, box]) => box.checked).map(([group]) => group) }
167703
+ }
167704
+
167705
+ try {
167706
+ const data = await api('/api/auth?pool=' + encodeURIComponent(r.id))
167707
+ holder.innerHTML = ''
167708
+
167709
+ // --------------------------------------------------------------
167710
+ // The user list, each row expanding into its group/password form.
167711
+
167712
+ if (data.users.length === 0) {
167713
+ holder.append($('p', { className: 'empty' }, 'The pool has no users.'))
167714
+ } else {
167715
+ const editRow = $('tr', { style: 'display: none' })
167716
+ const table = $('table', {}, [
167717
+ $('tr', {}, ['username', 'email', 'status', 'groups', 'created'].map(c => $('th', {}, c))),
167718
+ ])
167719
+
167720
+ for (const user of data.users) {
167721
+ const row = $('tr', {}, [
167722
+ $('td', {}, user.username),
167723
+ $('td', {}, user.email ?? ''),
167724
+ $('td', {}, user.enabled ? (user.status ?? '') : 'DISABLED'),
167725
+ $('td', {}, user.groups.join(', ')),
167726
+ $('td', {}, user.createdAt ? new Date(user.createdAt).toLocaleString() : ''),
167727
+ ])
167728
+
167729
+ row.style.cursor = 'pointer'
167730
+ row.onclick = () => {
167731
+ const groups = groupBoxes(data.groups, user.groups)
167732
+ const password = $('input', { type: 'password', placeholder: 'unchanged', spellcheck: false })
167733
+ const status = $('span', { className: 'status' })
167734
+ const save = $('button', { className: 'primary', textContent: 'Save' })
167735
+
167736
+ save.onclick = async () => {
167737
+ save.disabled = true
167738
+ try {
167739
+ await api('/api/auth/update', {
167740
+ method: 'POST',
167741
+ body: JSON.stringify({
167742
+ pool: r.id,
167743
+ username: user.username,
167744
+ password: password.value,
167745
+ groups: groups.selected(),
167746
+ }),
167747
+ })
167748
+ main.innerHTML = ''
167749
+ renderResource(main)
167750
+ return
167751
+ } catch (error) {
167752
+ status.textContent = String(error.message ?? error)
167753
+ }
167754
+ save.disabled = false
167755
+ }
167756
+
167757
+ editRow.innerHTML = ''
167758
+ editRow.style.display = ''
167759
+ editRow.append($('td', { colSpan: 5 }, $('div', { className: 'config-form' }, [
167760
+ $('label', { className: 'field' }, [$('span', { className: 'name' }, 'groups'), groups.row]),
167761
+ $('label', { className: 'field' }, [$('span', { className: 'name' }, 'new password'), password]),
167762
+ $('div', { className: 'actions' }, [save, status]),
167763
+ ])))
167764
+ row.after(editRow)
167765
+ }
167766
+
167767
+ table.append(row)
167768
+ }
167769
+
167770
+ holder.append(table)
167771
+ }
167772
+
167773
+ // --------------------------------------------------------------
167774
+ // Create a new user.
167775
+
167776
+ const username = $('input', { placeholder: 'username', spellcheck: false })
167777
+ const password = $('input', { type: 'password', placeholder: 'password', spellcheck: false })
167778
+ const groups = groupBoxes(data.groups, [])
167779
+ const status = $('span', { className: 'status' })
167780
+ const create = $('button', { className: 'primary', textContent: 'Create user' })
167781
+
167782
+ create.onclick = async () => {
167783
+ create.disabled = true
167784
+ try {
167785
+ await api('/api/auth/create', {
167786
+ method: 'POST',
167787
+ body: JSON.stringify({
167788
+ pool: r.id,
167789
+ username: username.value,
167790
+ password: password.value,
167791
+ groups: groups.selected(),
167792
+ }),
167793
+ })
167794
+ main.innerHTML = ''
167795
+ renderResource(main)
167796
+ return
167797
+ } catch (error) {
167798
+ status.textContent = String(error.message ?? error)
167799
+ }
167800
+ create.disabled = false
167801
+ }
167802
+
167803
+ holder.append(
167804
+ $('h3', {}, 'Create user'),
167805
+ $('div', { className: 'config-form' }, [
167806
+ $('label', { className: 'field' }, [$('span', { className: 'name' }, 'username'), username]),
167807
+ $('label', { className: 'field' }, [$('span', { className: 'name' }, 'password'), password]),
167808
+ $('label', { className: 'field' }, [$('span', { className: 'name' }, 'groups'), groups.row]),
167809
+ $('div', { className: 'actions' }, [create, status]),
167810
+ ]),
167811
+ )
167812
+ } catch (error) {
167813
+ holder.innerHTML = ''
167814
+ holder.append($('pre', { className: 'result error' }, String(error.message ?? error)))
167815
+ }
167816
+ }
167817
+
167460
167818
  const renderResource = main => {
167461
167819
  const r = view.resource
167462
167820
  const back = $('button', { className: 'back' }, '\u2190 ' + groupTitle(r.kind))
@@ -167480,6 +167838,7 @@ const renderResource = main => {
167480
167838
  if (r.kind === 'cache') return cachePanel(main, r)
167481
167839
  if (r.kind === 'store') return storePanel(main, r)
167482
167840
  if (r.kind === 'config') return configPanel(main)
167841
+ if (r.kind === 'auth') return authPanel(main, r)
167483
167842
  if (r.kind === 'route' || r.kind === 'site') {
167484
167843
  const url = r.url ?? r.detail
167485
167844
  main.append($('p', {}, $('a', { href: url, target: '_blank', style: 'color: var(--accent)' }, url)))
@@ -167861,6 +168220,36 @@ var createDashboardServer = (props) => {
167861
168220
  if (url.pathname === "/api/emails") {
167862
168221
  return { status: 200, body: JSON.stringify({ emails: props.getEmails?.() ?? [] }) };
167863
168222
  }
168223
+ if (url.pathname.startsWith("/api/auth") && props.auth) {
168224
+ const auth2 = props.auth;
168225
+ try {
168226
+ if (url.pathname === "/api/auth" && req.method === "GET") {
168227
+ const pool = url.searchParams.get("pool") ?? "";
168228
+ return {
168229
+ status: 200,
168230
+ body: JSON.stringify({
168231
+ ...auth2.describePool(pool),
168232
+ users: await auth2.listUsers(pool)
168233
+ })
168234
+ };
168235
+ }
168236
+ if (url.pathname === "/api/auth/create" && req.method === "POST") {
168237
+ const { pool, username, password: password2, groups } = JSON.parse((await readBody3(req)).toString() || "{}");
168238
+ await auth2.createUser(pool, { username, password: password2, groups: groups ?? [] });
168239
+ return { status: 200, body: JSON.stringify({ ok: true }) };
168240
+ }
168241
+ if (url.pathname === "/api/auth/update" && req.method === "POST") {
168242
+ const { pool, username, password: password2, groups } = JSON.parse((await readBody3(req)).toString() || "{}");
168243
+ await auth2.updateUser(pool, { username, password: password2 || undefined, groups: groups ?? [] });
168244
+ return { status: 200, body: JSON.stringify({ ok: true }) };
168245
+ }
168246
+ } catch (error3) {
168247
+ return {
168248
+ status: 400,
168249
+ body: JSON.stringify({ error: error3 instanceof Error ? error3.message : String(error3) })
168250
+ };
168251
+ }
168252
+ }
167864
168253
  if (url.pathname === "/api/config") {
167865
168254
  if (req.method === "PUT") {
167866
168255
  const values2 = JSON.parse((await readBody3(req)).toString() || "{}");
@@ -168245,7 +168634,21 @@ var startDev = async (props) => {
168245
168634
  workspace = await loadWorkspace3(directories.root);
168246
168635
  await buildAll();
168247
168636
  });
168248
- const worker = createBundleWorker({ buildDir, env: env4, functionName: bundleName });
168637
+ const emitWorkerLine = (line, error3 = false) => {
168638
+ dev.events.emit("worker", { date: Date.now(), line, error: error3 });
168639
+ };
168640
+ const worker = createBundleWorker({
168641
+ buildDir,
168642
+ env: env4,
168643
+ functionName: bundleName,
168644
+ onOutput: (line, stream2) => emitWorkerLine(line, stream2 === "stderr")
168645
+ });
168646
+ dev.resources.push({
168647
+ kind: "worker",
168648
+ id: "bundle",
168649
+ channel: "worker",
168650
+ detail: "The output & errors of the local bundle worker"
168651
+ });
168249
168652
  let dirty = false;
168250
168653
  let fresh;
168251
168654
  let restartNeeded = false;
@@ -168321,7 +168724,13 @@ var startDev = async (props) => {
168321
168724
  routers.push(await startDevRouter({
168322
168725
  routes: dev.routes.filter((route) => route.routerId === id),
168323
168726
  port,
168324
- dispatch
168727
+ dispatch,
168728
+ onError(error3, routeKey) {
168729
+ const detail = error3 instanceof Error ? error3.stack ?? error3.message : String(error3);
168730
+ process.stderr.write(`Route ${routeKey} failed: ${detail}
168731
+ `);
168732
+ emitWorkerLine(`Route ${routeKey} failed: ${detail}`, true);
168733
+ }
168325
168734
  }));
168326
168735
  }
168327
168736
  const dashboardPort = props.port;
@@ -168340,6 +168749,10 @@ var startDev = async (props) => {
168340
168749
  configFile: join54(directories.output, "local", "config.json"),
168341
168750
  getEmails: () => props.pool.peek("shim:ses-email")?.server.list() ?? [],
168342
168751
  configPulled: Object.keys(props.pool.peek("config:pull") ?? {}),
168752
+ auth: createAuthAdmin({
168753
+ appConfig,
168754
+ resolvedPools: () => props.pool.peek("auth:pull")
168755
+ }),
168343
168756
  events: dev.events
168344
168757
  });
168345
168758
  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.46",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -43,14 +43,14 @@
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@awsless/big-float": "^0.1.7",
46
- "@awsless/s3": "^0.0.22",
46
+ "@awsless/dynamodb": "^0.3.22",
47
47
  "@awsless/duration": "^0.0.4",
48
- "@awsless/validate": "^0.1.7",
48
+ "@awsless/s3": "^0.0.22",
49
49
  "@awsless/lambda": "^0.0.47",
50
- "@awsless/dynamodb": "^0.3.22",
51
50
  "@awsless/weak-cache": "^0.0.1",
52
- "awsless": "^0.0.15-local.11",
53
- "@awsless/json": "^0.0.11"
51
+ "@awsless/json": "^0.0.11",
52
+ "@awsless/validate": "^0.1.7",
53
+ "awsless": "^0.0.15-local.11"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@aws-sdk/client-cloudformation": "^3.369.0",
@@ -120,24 +120,24 @@
120
120
  "zip-a-folder": "^3.1.6",
121
121
  "zod": "^3.24.2",
122
122
  "zod-to-json-schema": "^3.24.3",
123
- "@awsless/big-float": "^0.1.7",
124
- "@awsless/cloudwatch": "^0.0.1",
125
123
  "@awsless/clui": "^0.0.9",
124
+ "@awsless/big-float": "^0.1.7",
126
125
  "@awsless/duration": "^0.0.4",
127
- "@awsless/json": "^0.0.11",
128
126
  "@awsless/lambda": "^0.0.47",
129
- "@awsless/redis": "^0.1.13",
130
- "@awsless/size": "^0.0.2",
131
- "@awsless/iot": "^0.0.5",
127
+ "@awsless/dynamodb": "^0.3.22",
132
128
  "@awsless/open-search": "^0.0.26",
129
+ "@awsless/iot": "^0.0.5",
130
+ "@awsless/cloudwatch": "^0.0.1",
131
+ "@awsless/json": "^0.0.11",
132
+ "@awsless/redis": "^0.1.13",
133
133
  "@awsless/s3": "^0.0.22",
134
- "@awsless/dynamodb": "^0.3.22",
135
- "@awsless/sns": "^0.0.11",
136
- "@awsless/scheduler": "^0.0.5",
137
134
  "@awsless/sqs": "^0.0.24",
135
+ "@awsless/size": "^0.0.2",
136
+ "@awsless/scheduler": "^0.0.5",
137
+ "@awsless/sns": "^0.0.11",
138
138
  "@awsless/validate": "^0.1.7",
139
- "@awsless/weak-cache": "^0.0.1",
140
139
  "awsless": "^0.0.15-local.11",
140
+ "@awsless/weak-cache": "^0.0.1",
141
141
  "@awsless/ssm": "^0.0.8"
142
142
  },
143
143
  "scripts": {