@awsless/cli 0.0.46-local.6 → 0.0.46-local.8

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 +145 -24
  2. package/package.json +10 -10
package/dist/bin.js CHANGED
@@ -150257,6 +150257,16 @@ var bootstrapAwsless = async (props) => {
150257
150257
  // src/config/load/load.ts
150258
150258
  import { basename as basename2, dirname as dirname5, join as join13 } from "path";
150259
150259
 
150260
+ // src/feature/config/schema.ts
150261
+ var ConfigNameSchema = exports_external.string().regex(/^[a-z0-9-]+$/, "Invalid config name");
150262
+ var ConfigsSchema = exports_external.array(ConfigNameSchema).optional().describe("Define the config values for your app.");
150263
+ var ConfigFileSchema = exports_external.record(ConfigNameSchema, exports_external.union([
150264
+ exports_external.string().describe("A committed default value for the local dev environment."),
150265
+ exports_external.object({
150266
+ secret: exports_external.literal(true).describe("Pulled from ssm into memory on local dev boot, never stored locally.")
150267
+ }).describe("A secret config value.")
150268
+ ])).describe("The config values of your app.");
150269
+
150260
150270
  // src/config/schema/config-ref.ts
150261
150271
  var ConfigRefSchema = exports_external.string().regex(/^config:[a-z0-9-]+$/, "Invalid config reference");
150262
150272
  var isConfigRef = (value) => {
@@ -150325,10 +150335,6 @@ var AuthDefaultSchema = exports_external.record(ResourceIdSchema, exports_extern
150325
150335
  }).default({}).describe("Specifies the validity duration for every JWT token.")
150326
150336
  })).default({}).describe("Define the authenticatable users in your app.");
150327
150337
 
150328
- // src/feature/config/schema.ts
150329
- var ConfigNameSchema = exports_external.string().regex(/^[a-z0-9-]+$/, "Invalid config name");
150330
- var ConfigsSchema = exports_external.array(ConfigNameSchema).optional().describe("Define the config values for your app.");
150331
-
150332
150338
  // src/feature/domain/schema.ts
150333
150339
  var DomainNameSchema = exports_external.string().regex(/[a-z\-\_\.]/g, "Invalid domain name").describe("Enter a fully qualified domain name, for example, www.example.com. You can optionally include a trailing dot. If you omit the trailing dot, Amazon Route 53 assumes that the domain name that you specify is fully qualified. This means that Route 53 treats www.example.com (without a trailing dot) and www.example.com. (with a trailing dot) as identical.");
150334
150340
  var DNSTypeSchema = exports_external.enum(["A", "AAAA", "CAA", "CNAME", "DS", "MX", "NAPTR", "NS", "PTR", "SOA", "SPF", "SRV", "TXT"]).describe("The DNS record type.");
@@ -152482,8 +152488,32 @@ var loadAppConfig = async (options) => {
152482
152488
  debug("Validate app config file");
152483
152489
  const app = await validateConfig(AppSchema, appConfig.file, appConfig.data);
152484
152490
  app.stage = options.stage;
152491
+ await loadConfigFile(app, root3, options.stage);
152485
152492
  return app;
152486
152493
  };
152494
+ var loadConfigFile = async (app, root3, stage) => {
152495
+ const ext2 = ["config.json", "config.jsonc", "config.json5"];
152496
+ const files = await glob(ext2, { cwd: root3 });
152497
+ const file2 = ext2.find((name) => files.includes(name));
152498
+ if (!file2) {
152499
+ return;
152500
+ }
152501
+ debug("Load config file:", color2.info(file2));
152502
+ const config = await readConfigWithStage(join13(root3, file2), stage);
152503
+ const entries2 = await validateConfig(ConfigFileSchema, config.file, config.data);
152504
+ const defaults2 = {};
152505
+ const secrets = [];
152506
+ for (const [name, value] of Object.entries(entries2)) {
152507
+ if (typeof value === "string") {
152508
+ defaults2[name] = value;
152509
+ } else {
152510
+ secrets.push(name);
152511
+ }
152512
+ }
152513
+ app.configs = [...new Set([...app.configs ?? [], ...Object.keys(entries2)])].sort();
152514
+ app.configDefaults = defaults2;
152515
+ app.configSecrets = secrets;
152516
+ };
152487
152517
  var loadStackConfigs = async (options) => {
152488
152518
  debug("Load stacks config files");
152489
152519
  const ext2 = "{json,jsonc,json5}";
@@ -154771,6 +154801,9 @@ var createSsmServer = (props) => {
154771
154801
  let closeServer;
154772
154802
  let log;
154773
154803
  const warned2 = new Set;
154804
+ let defaults2 = {};
154805
+ let pulled = {};
154806
+ let secrets = new Set;
154774
154807
  const loadValues = async () => {
154775
154808
  try {
154776
154809
  return JSON.parse(await readFile12(props.file, "utf8"));
@@ -154782,6 +154815,11 @@ var createSsmServer = (props) => {
154782
154815
  connect(logFn) {
154783
154816
  log = logFn;
154784
154817
  },
154818
+ setValues(next) {
154819
+ defaults2 = next.defaults;
154820
+ pulled = next.pulled;
154821
+ secrets = next.secrets;
154822
+ },
154785
154823
  async listen(port = 0) {
154786
154824
  server = createServer2((req, res) => {
154787
154825
  const chunks = [];
@@ -154801,12 +154839,12 @@ var createSsmServer = (props) => {
154801
154839
  const parameters = [];
154802
154840
  for (const name of Names ?? []) {
154803
154841
  const key = name.split("/").at(-1);
154804
- const value = values[key];
154842
+ const value = values[key] ?? defaults2[key] ?? pulled[key];
154805
154843
  if (typeof value === "string") {
154806
154844
  parameters.push({ Name: name, Type: "SecureString", Value: value });
154807
154845
  } else if (!warned2.has(key)) {
154808
154846
  warned2.add(key);
154809
- log?.(`The "${key}" config has no local value yet. Set it on the dashboard or in ${props.file}`);
154847
+ log?.(secrets.has(key) ? `The "${key}" config secret has no value - the ssm pull didn't provide one. Set it with "awsless config set ${key}" or override it on the dashboard.` : `The "${key}" config has no local value yet. Set it on the dashboard or add a default in config.jsonc`);
154810
154848
  }
154811
154849
  }
154812
154850
  res.writeHead(200, { "content-type": "application/x-amz-json-1.1" });
@@ -154992,9 +155030,10 @@ var configFeature = defineFeature({
154992
155030
  if (names.size === 0) {
154993
155031
  return;
154994
155032
  }
155033
+ const secrets = new Set(ctx.appConfig.configSecrets ?? []);
154995
155034
  for (const name of names) {
154996
155035
  ctx.addEnv(`CONFIG_${constantCase(name)}`, name);
154997
- ctx.registerResource({ kind: "config", id: name });
155036
+ ctx.registerResource({ kind: "config", id: name, detail: secrets.has(name) ? "secret" : "" });
154998
155037
  }
154999
155038
  const file2 = join18(directories.output, "local", "config.json");
155000
155039
  const { server, port } = await ctx.keep("shim:ssm", file2, async () => {
@@ -155003,6 +155042,38 @@ var configFeature = defineFeature({
155003
155042
  return { value: { server: server2, port: port2 }, stop: () => server2.stop() };
155004
155043
  });
155005
155044
  ctx.addEnv("AWS_ENDPOINT_URL_SSM", `http://127.0.0.1:${port}`);
155045
+ let pulled = {};
155046
+ if (secrets.size > 0) {
155047
+ pulled = await ctx.keep("config:pull", [...secrets].sort().join(","), async () => {
155048
+ const values = {};
155049
+ try {
155050
+ ctx.log(`Pulling ${secrets.size} config secret${secrets.size === 1 ? "" : "s"} from SSM...`);
155051
+ const credentials2 = await getCredentials(ctx.appConfig.profile);
155052
+ const store = new SsmStore({ credentials: credentials2, appConfig: ctx.appConfig });
155053
+ let timer;
155054
+ const all5 = await Promise.race([
155055
+ store.list(),
155056
+ new Promise((_3, reject) => {
155057
+ timer = setTimeout(() => reject(new Error("the pull timed out after 15s")), 15000);
155058
+ })
155059
+ ]).finally(() => clearTimeout(timer));
155060
+ for (const name of secrets) {
155061
+ if (typeof all5[name] === "string") {
155062
+ values[name] = all5[name];
155063
+ }
155064
+ }
155065
+ } catch (error3) {
155066
+ debug("Config secret pull failed", error3);
155067
+ ctx.log(`Couldn't pull the config secrets from SSM (${error3 instanceof Error ? error3.message : String(error3)}) - secret-backed features will fail when called.`);
155068
+ }
155069
+ return { value: values, stop: () => {} };
155070
+ });
155071
+ }
155072
+ server.setValues({
155073
+ defaults: ctx.appConfig.configDefaults ?? {},
155074
+ pulled,
155075
+ secrets
155076
+ });
155006
155077
  ctx.restartOnChange(file2);
155007
155078
  ctx.registerServer({
155008
155079
  name: "config",
@@ -159270,7 +159341,7 @@ var tableFeature = defineFeature({
159270
159341
  const sort2 = props.sort ? typeValue(props.sort) : "undefined";
159271
159342
  const indexes = props.indexes && Object.keys(props.indexes).length > 0 ? `{ ${Object.entries(props.indexes).map(([indexName, index]) => {
159272
159343
  const indexSort = index.sort ? `; sort: ${typeValue(index.sort)}` : "";
159273
- return `${indexName}: { hash: ${typeValue(index.hash)}${indexSort} }`;
159344
+ return `'${indexName}': { hash: ${typeValue(index.hash)}${indexSort} }`;
159274
159345
  }).join("; ")} }` : "undefined";
159275
159346
  list2.addType(name, `{
159276
159347
  readonly name: '${tableName}'
@@ -165788,7 +165859,7 @@ var watchConfig = async (options, resolve5, reject) => {
165788
165859
  await loadAppConfig(options);
165789
165860
  debug("Start watching...");
165790
165861
  const ext2 = "{json,jsonc,json5}";
165791
- const watcher = $watch([`app.${ext2}`, `**/stack.${ext2}`, `**/*.stack.${ext2}`], {
165862
+ const watcher = $watch([`app.${ext2}`, `config.${ext2}`, `**/stack.${ext2}`, `**/*.stack.${ext2}`], {
165792
165863
  cwd: directories.root,
165793
165864
  ignored: ["**/node_modules/**", "**/dist/**"],
165794
165865
  awaitWriteFinish: true
@@ -166477,12 +166548,16 @@ var dashboardHtml = `<!doctype html>
166477
166548
  .filters input { flex: 1; }
166478
166549
  .filters input::placeholder { color: var(--muted); }
166479
166550
  .filters .count { color: var(--muted); align-self: center; white-space: nowrap; }
166480
- .rows { display: flex; flex-direction: column; }
166551
+ /* The stack & name columns share one grid across every row, so the
166552
+ names line up no matter how long each stack name is. */
166553
+ .rows { display: grid; grid-template-columns: fit-content(280px) 1fr auto; }
166554
+ .rows > .empty { grid-column: 1 / -1; }
166481
166555
  .row {
166482
- display: flex;
166556
+ display: grid;
166557
+ grid-template-columns: subgrid;
166558
+ grid-column: 1 / -1;
166483
166559
  gap: 12px;
166484
166560
  align-items: baseline;
166485
- width: 100%;
166486
166561
  text-align: left;
166487
166562
  background: none;
166488
166563
  border: none;
@@ -166500,14 +166575,20 @@ var dashboardHtml = `<!doctype html>
166500
166575
  text-overflow: ellipsis;
166501
166576
  white-space: nowrap;
166502
166577
  }
166503
- .row .id { font-weight: bold; }
166578
+ .row .id {
166579
+ font-weight: bold;
166580
+ min-width: 0;
166581
+ overflow: hidden;
166582
+ text-overflow: ellipsis;
166583
+ white-space: nowrap;
166584
+ }
166504
166585
  .row .info {
166505
166586
  color: var(--muted);
166506
- margin-left: auto;
166587
+ justify-self: end;
166507
166588
  overflow: hidden;
166508
166589
  text-overflow: ellipsis;
166509
166590
  white-space: nowrap;
166510
- max-width: 45%;
166591
+ max-width: 100%;
166511
166592
  }
166512
166593
  textarea {
166513
166594
  width: 100%;
@@ -166800,6 +166881,19 @@ const rpcPanel = (main, r) => {
166800
166881
  }
166801
166882
 
166802
166883
  const query = $('select', {}, r.queries.map(name => $('option', { value: name, textContent: name })))
166884
+
166885
+ // The token rides the same "authentication" header the rpc client
166886
+ // sends, & sticks around per api so a page reload keeps it.
166887
+ const authKey = 'rpc-auth-token:' + r.routeKey
166888
+ const auth = $('input', {
166889
+ type: 'text',
166890
+ placeholder: 'Auth token (optional)',
166891
+ value: localStorage.getItem(authKey) ?? '',
166892
+ autocomplete: 'off',
166893
+ spellcheck: false,
166894
+ })
166895
+ auth.oninput = () => localStorage.setItem(authKey, auth.value)
166896
+
166803
166897
  const payload = $('textarea', { value: '{}', spellcheck: false })
166804
166898
  const status = $('span', { className: 'status' })
166805
166899
  const result = $('pre', { className: 'result', hidden: true })
@@ -166818,7 +166912,10 @@ const rpcPanel = (main, r) => {
166818
166912
  version: '2.0',
166819
166913
  rawPath: '/',
166820
166914
  requestContext: { http: { method: 'POST', userAgent: 'awsless dev dashboard', sourceIp: '127.0.0.1' } },
166821
- headers: { 'content-type': 'application/json' },
166915
+ headers: {
166916
+ 'content-type': 'application/json',
166917
+ ...(auth.value.trim() ? { authentication: auth.value.trim() } : {}),
166918
+ },
166822
166919
  body: JSON.stringify([{ name: query.value, ...(parsed === undefined ? {} : { payload: parsed }) }]),
166823
166920
  }
166824
166921
 
@@ -166842,7 +166939,7 @@ const rpcPanel = (main, r) => {
166842
166939
  }
166843
166940
 
166844
166941
  main.append(
166845
- $('div', { className: 'filters' }, [query]),
166942
+ $('div', { className: 'filters' }, [query, auth]),
166846
166943
  payload,
166847
166944
  $('div', { className: 'actions' }, [run, status]),
166848
166945
  result,
@@ -167094,12 +167191,25 @@ const configPanel = async (main) => {
167094
167191
  const names = [...new Set(state.resources.filter(r => r.kind === 'config').map(r => r.id))].sort()
167095
167192
  const data = await api('/api/config')
167096
167193
  const values = data.values ?? {}
167194
+ const defaults = data.defaults ?? {}
167195
+ const secrets = new Set(data.secrets ?? [])
167097
167196
 
167098
167197
  const inputs = new Map()
167099
167198
  const form = $('div', { className: 'config-form' })
167100
167199
 
167101
167200
  for (const name of names) {
167102
- const input = $('input', { value: values[name] ?? '', placeholder: 'not set', spellcheck: false })
167201
+ // An empty input falls through to the committed default or the
167202
+ // pulled ssm secret - the placeholder shows which one carries.
167203
+ const placeholder = secrets.has(name)
167204
+ ? 'pulled from ssm'
167205
+ : defaults[name] !== undefined ? 'default: ' + defaults[name] : 'not set'
167206
+
167207
+ const input = $('input', {
167208
+ value: values[name] ?? '',
167209
+ placeholder,
167210
+ spellcheck: false,
167211
+ ...(secrets.has(name) ? { type: 'password' } : {}),
167212
+ })
167103
167213
  inputs.set(name, input)
167104
167214
  form.append($('label', { className: 'field' }, [$('span', { className: 'name' }, name), input]))
167105
167215
  }
@@ -167541,7 +167651,14 @@ var createDashboardServer = (props) => {
167541
167651
  try {
167542
167652
  values = JSON.parse(await readFile21(props.configFile, "utf8"));
167543
167653
  } catch (_4) {}
167544
- return { status: 200, body: JSON.stringify({ values }) };
167654
+ return {
167655
+ status: 200,
167656
+ body: JSON.stringify({
167657
+ values,
167658
+ defaults: props.configMeta?.defaults ?? {},
167659
+ secrets: props.configMeta?.secrets ?? []
167660
+ })
167661
+ };
167545
167662
  }
167546
167663
  return { status: 404, body: JSON.stringify({ error: `Unknown dashboard path: ${url.pathname}` }) };
167547
167664
  };
@@ -167964,6 +168081,10 @@ var startDev = async (props) => {
167964
168081
  env: env4,
167965
168082
  storeRoot: join53(directories.output, "local", "store"),
167966
168083
  configFile: join53(directories.output, "local", "config.json"),
168084
+ configMeta: {
168085
+ defaults: appConfig.configDefaults ?? {},
168086
+ secrets: appConfig.configSecrets ?? []
168087
+ },
167967
168088
  events: dev.events
167968
168089
  });
167969
168090
  dashboard.connect(dispatch);
@@ -168224,7 +168345,7 @@ import { DynamoDBClient as DynamoDBClient6, dynamoDBClient } from "@awsless/dyna
168224
168345
 
168225
168346
  // ../../node_modules/.pnpm/@aws-sdk+client-iot-data-plane@3.1023.0_aws-crt@1.30.0_supports-color@8.1.1_/node_modules/@aws-sdk/client-iot-data-plane/dist-es/IoTDataPlaneClient.js
168226
168347
  var import_core50 = __toESM(require_dist_cjs2(), 1);
168227
- var import_schema67 = __toESM(require_schema(), 1);
168348
+ var import_schema68 = __toESM(require_schema(), 1);
168228
168349
 
168229
168350
  // ../../node_modules/.pnpm/@aws-sdk+client-iot-data-plane@3.1023.0_aws-crt@1.30.0_supports-color@8.1.1_/node_modules/@aws-sdk/client-iot-data-plane/dist-es/auth/httpAuthSchemeProvider.js
168230
168351
  var import_httpAuthSchemes4 = __toESM(require_httpAuthSchemes(), 1);
@@ -168430,7 +168551,7 @@ var defaultEndpointResolver3 = (endpointParams, context = {}) => {
168430
168551
  customEndpointFunctions.aws = awsEndpointFunctions;
168431
168552
 
168432
168553
  // ../../node_modules/.pnpm/@aws-sdk+client-iot-data-plane@3.1023.0_aws-crt@1.30.0_supports-color@8.1.1_/node_modules/@aws-sdk/client-iot-data-plane/dist-es/schemas/schemas_0.js
168433
- var import_schema66 = __toESM(require_schema(), 1);
168554
+ var import_schema67 = __toESM(require_schema(), 1);
168434
168555
 
168435
168556
  // ../../node_modules/.pnpm/@aws-sdk+client-iot-data-plane@3.1023.0_aws-crt@1.30.0_supports-color@8.1.1_/node_modules/@aws-sdk/client-iot-data-plane/dist-es/models/IoTDataPlaneServiceException.js
168436
168557
  class IoTDataPlaneServiceException extends ServiceException {
@@ -168660,10 +168781,10 @@ var _xamcd = "x-amz-mqtt5-correlation-data";
168660
168781
  var _xampfi = "x-amz-mqtt5-payload-format-indicator";
168661
168782
  var _xamup = "x-amz-mqtt5-user-properties";
168662
168783
  var n03 = "com.amazonaws.iotdataplane";
168663
- var _s_registry3 = import_schema66.TypeRegistry.for(_s3);
168784
+ var _s_registry3 = import_schema67.TypeRegistry.for(_s3);
168664
168785
  var IoTDataPlaneServiceException$ = [-3, _s3, "IoTDataPlaneServiceException", 0, [], []];
168665
168786
  _s_registry3.registerError(IoTDataPlaneServiceException$, IoTDataPlaneServiceException);
168666
- var n0_registry3 = import_schema66.TypeRegistry.for(n03);
168787
+ var n0_registry3 = import_schema67.TypeRegistry.for(n03);
168667
168788
  var ConflictException$ = [
168668
168789
  -3,
168669
168790
  n03,
@@ -169099,7 +169220,7 @@ class IoTDataPlaneClient extends Client {
169099
169220
  const _config_7 = resolveHttpAuthSchemeConfig3(_config_6);
169100
169221
  const _config_8 = resolveRuntimeExtensions3(_config_7, configuration?.extensions || []);
169101
169222
  this.config = _config_8;
169102
- this.middlewareStack.use(import_schema67.getSchemaSerdePlugin(this.config));
169223
+ this.middlewareStack.use(import_schema68.getSchemaSerdePlugin(this.config));
169103
169224
  this.middlewareStack.use(getUserAgentPlugin(this.config));
169104
169225
  this.middlewareStack.use(getRetryPlugin(this.config));
169105
169226
  this.middlewareStack.use(getContentLengthPlugin(this.config));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awsless/cli",
3
- "version": "0.0.46-local.6",
3
+ "version": "0.0.46-local.8",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -43,9 +43,9 @@
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@awsless/big-float": "^0.1.7",
46
- "@awsless/duration": "^0.0.4",
47
46
  "@awsless/dynamodb": "^0.3.22",
48
47
  "@awsless/lambda": "^0.0.47",
48
+ "@awsless/duration": "^0.0.4",
49
49
  "@awsless/s3": "^0.0.22",
50
50
  "@awsless/json": "^0.0.11",
51
51
  "@awsless/validate": "^0.1.7",
@@ -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/clui": "^0.0.9",
125
124
  "@awsless/cloudwatch": "^0.0.1",
125
+ "@awsless/clui": "^0.0.9",
126
126
  "@awsless/duration": "^0.0.4",
127
127
  "@awsless/dynamodb": "^0.3.22",
128
- "@awsless/iot": "^0.0.5",
129
128
  "@awsless/lambda": "^0.0.47",
130
- "@awsless/open-search": "^0.0.24",
131
- "@awsless/s3": "^0.0.22",
132
129
  "@awsless/json": "^0.0.11",
130
+ "@awsless/s3": "^0.0.22",
131
+ "@awsless/open-search": "^0.0.24",
132
+ "@awsless/scheduler": "^0.0.5",
133
133
  "@awsless/redis": "^0.1.13",
134
134
  "@awsless/size": "^0.0.2",
135
- "@awsless/scheduler": "^0.0.5",
136
- "@awsless/validate": "^0.1.7",
137
135
  "@awsless/sns": "^0.0.11",
138
- "@awsless/ssm": "^0.0.8",
139
136
  "@awsless/sqs": "^0.0.24",
137
+ "@awsless/iot": "^0.0.5",
138
+ "@awsless/validate": "^0.1.7",
140
139
  "@awsless/weak-cache": "^0.0.1",
141
- "awsless": "^0.0.15-local.3"
140
+ "awsless": "^0.0.15-local.3",
141
+ "@awsless/ssm": "^0.0.8"
142
142
  },
143
143
  "scripts": {
144
144
  "test": "bun cli/build-handlers.ts && pnpm vitest",