@softr/cli 0.1.4 → 0.1.6-se21219.17

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 (3) hide show
  1. package/README.md +32 -9
  2. package/dist/cli.mjs +961 -185
  3. package/package.json +2 -2
package/dist/cli.mjs CHANGED
@@ -1745,7 +1745,7 @@ var require_table = __commonJS({
1745
1745
  var debug = require_debug();
1746
1746
  var utils = require_utils();
1747
1747
  var tableLayout = require_layout_manager();
1748
- var Table8 = class extends Array {
1748
+ var Table9 = class extends Array {
1749
1749
  constructor(opts) {
1750
1750
  super();
1751
1751
  const options = utils.mergeOptions(opts);
@@ -1820,7 +1820,7 @@ var require_table = __commonJS({
1820
1820
  return str[0].length;
1821
1821
  }
1822
1822
  };
1823
- Table8.reset = () => debug.reset();
1823
+ Table9.reset = () => debug.reset();
1824
1824
  function doDraw(row, lineNum, result) {
1825
1825
  let line = [];
1826
1826
  row.forEach(function(cell) {
@@ -1829,7 +1829,7 @@ var require_table = __commonJS({
1829
1829
  let str = line.join("");
1830
1830
  if (str.length) result.push(str);
1831
1831
  }
1832
- module.exports = Table8;
1832
+ module.exports = Table9;
1833
1833
  }
1834
1834
  });
1835
1835
 
@@ -2598,14 +2598,14 @@ var Option = class {
2598
2598
  * @param {string} flags
2599
2599
  * @param {string} [description]
2600
2600
  */
2601
- constructor(flags, description) {
2602
- this.flags = flags;
2601
+ constructor(flags2, description) {
2602
+ this.flags = flags2;
2603
2603
  this.description = description || "";
2604
- this.required = flags.includes("<");
2605
- this.optional = flags.includes("[");
2606
- this.variadic = /\w\.\.\.[>\]]$/.test(flags);
2604
+ this.required = flags2.includes("<");
2605
+ this.optional = flags2.includes("[");
2606
+ this.variadic = /\w\.\.\.[>\]]$/.test(flags2);
2607
2607
  this.mandatory = false;
2608
- const optionFlags = splitOptionFlags(flags);
2608
+ const optionFlags = splitOptionFlags(flags2);
2609
2609
  this.short = optionFlags.shortFlag;
2610
2610
  this.long = optionFlags.longFlag;
2611
2611
  this.negate = false;
@@ -2661,8 +2661,8 @@ var Option = class {
2661
2661
  * @param {(string | string[])} names
2662
2662
  * @return {Option}
2663
2663
  */
2664
- conflicts(names) {
2665
- this.conflictsWith = this.conflictsWith.concat(names);
2664
+ conflicts(names2) {
2665
+ this.conflictsWith = this.conflictsWith.concat(names2);
2666
2666
  return this;
2667
2667
  }
2668
2668
  /**
@@ -2856,12 +2856,12 @@ function camelcase(str) {
2856
2856
  return str2 + word[0].toUpperCase() + word.slice(1);
2857
2857
  });
2858
2858
  }
2859
- function splitOptionFlags(flags) {
2859
+ function splitOptionFlags(flags2) {
2860
2860
  let shortFlag;
2861
2861
  let longFlag;
2862
2862
  const shortFlagExp = /^-[^-]$/;
2863
2863
  const longFlagExp = /^--[^-]/;
2864
- const flagParts = flags.split(/[ |,]+/).concat("guard");
2864
+ const flagParts = flags2.split(/[ |,]+/).concat("guard");
2865
2865
  if (shortFlagExp.test(flagParts[0])) shortFlag = flagParts.shift();
2866
2866
  if (longFlagExp.test(flagParts[0])) longFlag = flagParts.shift();
2867
2867
  if (!shortFlag && shortFlagExp.test(flagParts[0]))
@@ -2872,7 +2872,7 @@ function splitOptionFlags(flags) {
2872
2872
  }
2873
2873
  if (flagParts[0].startsWith("-")) {
2874
2874
  const unsupportedFlag = flagParts[0];
2875
- const baseError = `option creation failed due to '${unsupportedFlag}' in option flags '${flags}'`;
2875
+ const baseError = `option creation failed due to '${unsupportedFlag}' in option flags '${flags2}'`;
2876
2876
  if (/^-[^-][^-]/.test(unsupportedFlag))
2877
2877
  throw new Error(
2878
2878
  `${baseError}
@@ -2891,7 +2891,7 @@ function splitOptionFlags(flags) {
2891
2891
  }
2892
2892
  if (shortFlag === void 0 && longFlag === void 0)
2893
2893
  throw new Error(
2894
- `option creation failed due to no flags found in '${flags}'.`
2894
+ `option creation failed due to no flags found in '${flags2}'.`
2895
2895
  );
2896
2896
  return { shortFlag, longFlag };
2897
2897
  }
@@ -3268,8 +3268,8 @@ var Command = class _Command extends EventEmitter {
3268
3268
  * @param {string} names
3269
3269
  * @return {Command} `this` command for chaining
3270
3270
  */
3271
- arguments(names) {
3272
- names.trim().split(/ +/).forEach((detail) => {
3271
+ arguments(names2) {
3272
+ names2.trim().split(/ +/).forEach((detail) => {
3273
3273
  this.argument(detail);
3274
3274
  });
3275
3275
  return this;
@@ -3454,8 +3454,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
3454
3454
  * @param {string} [description]
3455
3455
  * @return {Option} new option
3456
3456
  */
3457
- createOption(flags, description) {
3458
- return new Option(flags, description);
3457
+ createOption(flags2, description) {
3458
+ return new Option(flags2, description);
3459
3459
  }
3460
3460
  /**
3461
3461
  * Wrap parseArgs to catch 'commander.invalidArgument'.
@@ -3570,13 +3570,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
3570
3570
  * @return {Command} `this` command for chaining
3571
3571
  * @private
3572
3572
  */
3573
- _optionEx(config, flags, description, fn, defaultValue) {
3574
- if (typeof flags === "object" && flags instanceof Option) {
3573
+ _optionEx(config, flags2, description, fn, defaultValue) {
3574
+ if (typeof flags2 === "object" && flags2 instanceof Option) {
3575
3575
  throw new Error(
3576
3576
  "To add an Option object use addOption() instead of option() or requiredOption()"
3577
3577
  );
3578
3578
  }
3579
- const option = this.createOption(flags, description);
3579
+ const option = this.createOption(flags2, description);
3580
3580
  option.makeOptionMandatory(!!config.mandatory);
3581
3581
  if (typeof fn === "function") {
3582
3582
  option.default(defaultValue).argParser(fn);
@@ -3613,8 +3613,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
3613
3613
  * @param {*} [defaultValue]
3614
3614
  * @return {Command} `this` command for chaining
3615
3615
  */
3616
- option(flags, description, parseArg, defaultValue) {
3617
- return this._optionEx({}, flags, description, parseArg, defaultValue);
3616
+ option(flags2, description, parseArg, defaultValue) {
3617
+ return this._optionEx({}, flags2, description, parseArg, defaultValue);
3618
3618
  }
3619
3619
  /**
3620
3620
  * Add a required option which must have a value after parsing. This usually means
@@ -3628,10 +3628,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
3628
3628
  * @param {*} [defaultValue]
3629
3629
  * @return {Command} `this` command for chaining
3630
3630
  */
3631
- requiredOption(flags, description, parseArg, defaultValue) {
3631
+ requiredOption(flags2, description, parseArg, defaultValue) {
3632
3632
  return this._optionEx(
3633
3633
  { mandatory: true },
3634
- flags,
3634
+ flags2,
3635
3635
  description,
3636
3636
  parseArg,
3637
3637
  defaultValue
@@ -3798,18 +3798,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
3798
3798
  *
3799
3799
  * @private
3800
3800
  */
3801
- _prepareUserArgs(argv, parseOptions) {
3801
+ _prepareUserArgs(argv, parseOptions2) {
3802
3802
  if (argv !== void 0 && !Array.isArray(argv)) {
3803
3803
  throw new Error("first parameter to parse must be array or undefined");
3804
3804
  }
3805
- parseOptions = parseOptions || {};
3806
- if (argv === void 0 && parseOptions.from === void 0) {
3805
+ parseOptions2 = parseOptions2 || {};
3806
+ if (argv === void 0 && parseOptions2.from === void 0) {
3807
3807
  if (process2.versions?.electron) {
3808
- parseOptions.from = "electron";
3808
+ parseOptions2.from = "electron";
3809
3809
  }
3810
3810
  const execArgv = process2.execArgv ?? [];
3811
3811
  if (execArgv.includes("-e") || execArgv.includes("--eval") || execArgv.includes("-p") || execArgv.includes("--print")) {
3812
- parseOptions.from = "eval";
3812
+ parseOptions2.from = "eval";
3813
3813
  }
3814
3814
  }
3815
3815
  if (argv === void 0) {
@@ -3817,7 +3817,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3817
3817
  }
3818
3818
  this.rawArgs = argv.slice();
3819
3819
  let userArgs;
3820
- switch (parseOptions.from) {
3820
+ switch (parseOptions2.from) {
3821
3821
  case void 0:
3822
3822
  case "node":
3823
3823
  this._scriptPath = argv[1];
@@ -3839,7 +3839,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3839
3839
  break;
3840
3840
  default:
3841
3841
  throw new Error(
3842
- `unexpected parse option { from: '${parseOptions.from}' }`
3842
+ `unexpected parse option { from: '${parseOptions2.from}' }`
3843
3843
  );
3844
3844
  }
3845
3845
  if (!this._name && this._scriptPath)
@@ -3869,9 +3869,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3869
3869
  * @param {string} [parseOptions.from] - where the args are from: 'node', 'user', 'electron'
3870
3870
  * @return {Command} `this` command for chaining
3871
3871
  */
3872
- parse(argv, parseOptions) {
3872
+ parse(argv, parseOptions2) {
3873
3873
  this._prepareForParse();
3874
- const userArgs = this._prepareUserArgs(argv, parseOptions);
3874
+ const userArgs = this._prepareUserArgs(argv, parseOptions2);
3875
3875
  this._parseCommand([], userArgs);
3876
3876
  return this;
3877
3877
  }
@@ -3895,9 +3895,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3895
3895
  * @param {string} parseOptions.from - where the args are from: 'node', 'user', 'electron'
3896
3896
  * @return {Promise}
3897
3897
  */
3898
- async parseAsync(argv, parseOptions) {
3898
+ async parseAsync(argv, parseOptions2) {
3899
3899
  this._prepareForParse();
3900
- const userArgs = this._prepareUserArgs(argv, parseOptions);
3900
+ const userArgs = this._prepareUserArgs(argv, parseOptions2);
3901
3901
  await this._parseCommand([], userArgs);
3902
3902
  return this;
3903
3903
  }
@@ -4748,12 +4748,12 @@ Expecting one of '${allowedValues.join("', '")}'`);
4748
4748
  * @param {string} [description]
4749
4749
  * @return {(this | string | undefined)} `this` command for chaining, or version string if no arguments
4750
4750
  */
4751
- version(str, flags, description) {
4751
+ version(str, flags2, description) {
4752
4752
  if (str === void 0) return this._version;
4753
4753
  this._version = str;
4754
- flags = flags || "-V, --version";
4754
+ flags2 = flags2 || "-V, --version";
4755
4755
  description = description || "output the version number";
4756
- const versionOption = this.createOption(flags, description);
4756
+ const versionOption = this.createOption(flags2, description);
4757
4757
  this._versionOptionName = versionOption.attributeName();
4758
4758
  this._registerOption(versionOption);
4759
4759
  this.on("option:" + versionOption.name(), () => {
@@ -4951,9 +4951,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
4951
4951
  * @param {string} [path]
4952
4952
  * @return {(string|null|Command)}
4953
4953
  */
4954
- executableDir(path15) {
4955
- if (path15 === void 0) return this._executableDir;
4956
- this._executableDir = path15;
4954
+ executableDir(path16) {
4955
+ if (path16 === void 0) return this._executableDir;
4956
+ this._executableDir = path16;
4957
4957
  return this;
4958
4958
  }
4959
4959
  /**
@@ -5055,9 +5055,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
5055
5055
  * @param {string} [description]
5056
5056
  * @return {Command} `this` command for chaining
5057
5057
  */
5058
- helpOption(flags, description) {
5059
- if (typeof flags === "boolean") {
5060
- if (flags) {
5058
+ helpOption(flags2, description) {
5059
+ if (typeof flags2 === "boolean") {
5060
+ if (flags2) {
5061
5061
  if (this._helpOption === null) this._helpOption = void 0;
5062
5062
  if (this._defaultOptionGroup) {
5063
5063
  this._initOptionGroup(this._getHelpOption());
@@ -5068,10 +5068,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
5068
5068
  return this;
5069
5069
  }
5070
5070
  this._helpOption = this.createOption(
5071
- flags ?? "-h, --help",
5071
+ flags2 ?? "-h, --help",
5072
5072
  description ?? "display help for command"
5073
5073
  );
5074
- if (flags || description) this._initOptionGroup(this._helpOption);
5074
+ if (flags2 || description) this._initOptionGroup(this._helpOption);
5075
5075
  return this;
5076
5076
  }
5077
5077
  /**
@@ -5310,12 +5310,12 @@ function executionOrder(config) {
5310
5310
  collect(config.actions, []);
5311
5311
  const outgoing = new Map(appearance.map((id) => [id, []]));
5312
5312
  const indegree = new Map(appearance.map((id) => [id, 0]));
5313
- for (const path15 of pathSets.flat()) {
5314
- const to = path15.toActionId;
5315
- if (!to || !nodes.has(path15.fromActionId) || !nodes.has(to) || outgoing.get(path15.fromActionId).includes(to)) {
5313
+ for (const path16 of pathSets.flat()) {
5314
+ const to = path16.toActionId;
5315
+ if (!to || !nodes.has(path16.fromActionId) || !nodes.has(to) || outgoing.get(path16.fromActionId).includes(to)) {
5316
5316
  continue;
5317
5317
  }
5318
- outgoing.get(path15.fromActionId).push(to);
5318
+ outgoing.get(path16.fromActionId).push(to);
5319
5319
  indegree.set(to, indegree.get(to) + 1);
5320
5320
  }
5321
5321
  const queue = [...nodes.values()].filter((node) => node.kind === "trigger").map((node) => node.id);
@@ -6130,43 +6130,45 @@ var BASE_URLS = {
6130
6130
  studio: (target) => target.studioApiUrl,
6131
6131
  tables: (target) => target.tablesApiUrl
6132
6132
  };
6133
- async function send(client, service, method, path15, body) {
6133
+ async function send(client, service, method, path16, body) {
6134
6134
  const init = {
6135
6135
  method,
6136
6136
  headers: { ...authHeaders(client, service), Accept: "application/json" }
6137
6137
  };
6138
- if (body !== void 0) {
6138
+ if (body instanceof FormData) {
6139
+ init.body = body;
6140
+ } else if (body !== void 0) {
6139
6141
  init.headers = { ...init.headers, "Content-Type": "application/json" };
6140
6142
  init.body = JSON.stringify(body);
6141
6143
  }
6142
- const response = await fetch(`${BASE_URLS[service](client.target)}${path15}`, init);
6144
+ const response = await fetch(`${BASE_URLS[service](client.target)}${path16}`, init);
6143
6145
  if (response.ok) {
6144
6146
  return response;
6145
6147
  }
6146
6148
  const text = await response.text();
6147
6149
  throw new SoftrApiError(
6148
- `${method} ${path15} failed with status ${response.status}${text ? `: ${text}` : ""}`,
6150
+ `${method} ${path16} failed with status ${response.status}${text ? `: ${text}` : ""}`,
6149
6151
  response.status,
6150
6152
  text,
6151
6153
  hintFor(response.status, client, service)
6152
6154
  );
6153
6155
  }
6154
- async function apiRequest(client, method, path15, body) {
6155
- const response = await send(client, "workflows", method, path15, body);
6156
+ async function apiRequest(client, method, path16, body) {
6157
+ const response = await send(client, "workflows", method, path16, body);
6156
6158
  return await response.json();
6157
6159
  }
6158
- async function apiRequestVoid(client, method, path15, body) {
6159
- await send(client, "workflows", method, path15, body);
6160
+ async function apiRequestVoid(client, method, path16, body) {
6161
+ await send(client, "workflows", method, path16, body);
6160
6162
  }
6161
- async function tablesRequest(client, method, path15, body) {
6162
- const response = await send(client, "tables", method, path15, body);
6163
+ async function tablesRequest(client, method, path16, body) {
6164
+ const response = await send(client, "tables", method, path16, body);
6163
6165
  return await response.json();
6164
6166
  }
6165
- async function tablesRequestVoid(client, method, path15, body) {
6166
- await send(client, "tables", method, path15, body);
6167
+ async function tablesRequestVoid(client, method, path16, body) {
6168
+ await send(client, "tables", method, path16, body);
6167
6169
  }
6168
- async function studioRequest(client, method, path15, body) {
6169
- const response = await send(client, "studio", method, path15, body);
6170
+ async function studioRequest(client, method, path16, body) {
6171
+ const response = await send(client, "studio", method, path16, body);
6170
6172
  return await response.json();
6171
6173
  }
6172
6174
 
@@ -6177,6 +6179,9 @@ function globalOptions(command) {
6177
6179
  function emit(options, human, data2) {
6178
6180
  process.stdout.write(options.json ? JSON.stringify(data2, null, 2) + "\n" : human + "\n");
6179
6181
  }
6182
+ function plainTable(head, colWidths) {
6183
+ return { head, colWidths, wordWrap: true, style: { head: [], border: [], "padding-left": 1, "padding-right": 1 } };
6184
+ }
6180
6185
  function reportError(error) {
6181
6186
  if (error instanceof CliError || error instanceof SoftrApiError) {
6182
6187
  process.stderr.write(`Error: ${error.message}
@@ -6989,12 +6994,12 @@ function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {
6989
6994
  }
6990
6995
  return result;
6991
6996
  }
6992
- function findNodeAtLocation(root, path15) {
6997
+ function findNodeAtLocation(root, path16) {
6993
6998
  if (!root) {
6994
6999
  return void 0;
6995
7000
  }
6996
7001
  let node = root;
6997
- for (let segment of path15) {
7002
+ for (let segment of path16) {
6998
7003
  if (typeof segment === "string") {
6999
7004
  if (node.type !== "object" || !Array.isArray(node.children)) {
7000
7005
  return void 0;
@@ -7348,14 +7353,14 @@ function getNodeType(value) {
7348
7353
 
7349
7354
  // node_modules/jsonc-parser/lib/esm/impl/edit.js
7350
7355
  function setProperty(text, originalPath, value, options) {
7351
- const path15 = originalPath.slice();
7356
+ const path16 = originalPath.slice();
7352
7357
  const errors = [];
7353
7358
  const root = parseTree(text, errors);
7354
7359
  let parent = void 0;
7355
7360
  let lastSegment = void 0;
7356
- while (path15.length > 0) {
7357
- lastSegment = path15.pop();
7358
- parent = findNodeAtLocation(root, path15);
7361
+ while (path16.length > 0) {
7362
+ lastSegment = path16.pop();
7363
+ parent = findNodeAtLocation(root, path16);
7359
7364
  if (parent === void 0 && value !== void 0) {
7360
7365
  if (typeof lastSegment === "string") {
7361
7366
  value = { [lastSegment]: value };
@@ -7578,8 +7583,8 @@ function printParseErrorCode(code) {
7578
7583
  }
7579
7584
  return "<unknown ParseErrorCode>";
7580
7585
  }
7581
- function modify(text, path15, value, options) {
7582
- return setProperty(text, path15, value, options);
7586
+ function modify(text, path16, value, options) {
7587
+ return setProperty(text, path16, value, options);
7583
7588
  }
7584
7589
  function applyEdits(text, edits) {
7585
7590
  let sortedEdits = edits.slice(0).sort((a, b) => {
@@ -7636,8 +7641,8 @@ function parseJsoncFile(file, text) {
7636
7641
  async function listSubdirsWith(cwd, holds) {
7637
7642
  const entries = await readdir(cwd, { withFileTypes: true });
7638
7643
  const dirs = entries.filter((entry) => entry.isDirectory()).map((entry) => path2.join(cwd, entry.name));
7639
- const flags = await Promise.all(dirs.map((dir) => holds(dir)));
7640
- return dirs.filter((_, index) => flags[index]);
7644
+ const flags2 = await Promise.all(dirs.map((dir) => holds(dir)));
7645
+ return dirs.filter((_, index) => flags2[index]);
7641
7646
  }
7642
7647
  async function readConfig(dir) {
7643
7648
  const file = configPath(dir);
@@ -8284,12 +8289,12 @@ prototypeAccessors.inAsync.get = function() {
8284
8289
  prototypeAccessors.canAwait.get = function() {
8285
8290
  for (var i2 = this.scopeStack.length - 1; i2 >= 0; i2--) {
8286
8291
  var ref2 = this.scopeStack[i2];
8287
- var flags = ref2.flags;
8288
- if (flags & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT)) {
8292
+ var flags2 = ref2.flags;
8293
+ if (flags2 & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT)) {
8289
8294
  return false;
8290
8295
  }
8291
- if (flags & SCOPE_FUNCTION) {
8292
- return (flags & SCOPE_ASYNC) > 0;
8296
+ if (flags2 & SCOPE_FUNCTION) {
8297
+ return (flags2 & SCOPE_ASYNC) > 0;
8293
8298
  }
8294
8299
  }
8295
8300
  return this.inModule && this.options.ecmaVersion >= 13 || this.options.allowAwaitOutsideFunction;
@@ -8305,8 +8310,8 @@ prototypeAccessors.allowReturn.get = function() {
8305
8310
  };
8306
8311
  prototypeAccessors.allowSuper.get = function() {
8307
8312
  var ref2 = this.currentThisScope();
8308
- var flags = ref2.flags;
8309
- return (flags & SCOPE_SUPER) > 0 || this.options.allowSuperOutsideMethod;
8313
+ var flags2 = ref2.flags;
8314
+ return (flags2 & SCOPE_SUPER) > 0 || this.options.allowSuperOutsideMethod;
8310
8315
  };
8311
8316
  prototypeAccessors.allowDirectSuper.get = function() {
8312
8317
  return (this.currentThisScope().flags & SCOPE_DIRECT_SUPER) > 0;
@@ -8317,8 +8322,8 @@ prototypeAccessors.treatFunctionsAsVar.get = function() {
8317
8322
  prototypeAccessors.allowNewDotTarget.get = function() {
8318
8323
  for (var i2 = this.scopeStack.length - 1; i2 >= 0; i2--) {
8319
8324
  var ref2 = this.scopeStack[i2];
8320
- var flags = ref2.flags;
8321
- if (flags & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT) || flags & SCOPE_FUNCTION && !(flags & SCOPE_ARROW)) {
8325
+ var flags2 = ref2.flags;
8326
+ if (flags2 & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT) || flags2 & SCOPE_FUNCTION && !(flags2 & SCOPE_ARROW)) {
8322
8327
  return true;
8323
8328
  }
8324
8329
  }
@@ -8326,11 +8331,11 @@ prototypeAccessors.allowNewDotTarget.get = function() {
8326
8331
  };
8327
8332
  prototypeAccessors.allowUsing.get = function() {
8328
8333
  var ref2 = this.currentScope();
8329
- var flags = ref2.flags;
8330
- if (flags & SCOPE_SWITCH) {
8334
+ var flags2 = ref2.flags;
8335
+ if (flags2 & SCOPE_SWITCH) {
8331
8336
  return false;
8332
8337
  }
8333
- if (!this.inModule && flags & SCOPE_TOP) {
8338
+ if (!this.inModule && flags2 & SCOPE_TOP) {
8334
8339
  return false;
8335
8340
  }
8336
8341
  return true;
@@ -11111,14 +11116,14 @@ pp$4.curPosition = function() {
11111
11116
  }
11112
11117
  };
11113
11118
  var pp$3 = Parser.prototype;
11114
- var Scope = function Scope2(flags) {
11115
- this.flags = flags;
11119
+ var Scope = function Scope2(flags2) {
11120
+ this.flags = flags2;
11116
11121
  this.var = [];
11117
11122
  this.lexical = [];
11118
11123
  this.functions = [];
11119
11124
  };
11120
- pp$3.enterScope = function(flags) {
11121
- this.scopeStack.push(new Scope(flags));
11125
+ pp$3.enterScope = function(flags2) {
11126
+ this.scopeStack.push(new Scope(flags2));
11122
11127
  };
11123
11128
  pp$3.exitScope = function() {
11124
11129
  this.scopeStack.pop();
@@ -11334,12 +11339,12 @@ var RegExpValidationState = function RegExpValidationState2(parser) {
11334
11339
  this.backReferenceNames = [];
11335
11340
  this.branchID = null;
11336
11341
  };
11337
- RegExpValidationState.prototype.reset = function reset(start, pattern, flags) {
11338
- var unicodeSets = flags.indexOf("v") !== -1;
11339
- var unicode = flags.indexOf("u") !== -1;
11342
+ RegExpValidationState.prototype.reset = function reset(start, pattern, flags2) {
11343
+ var unicodeSets = flags2.indexOf("v") !== -1;
11344
+ var unicode = flags2.indexOf("u") !== -1;
11340
11345
  this.start = start | 0;
11341
11346
  this.source = pattern + "";
11342
- this.flags = flags;
11347
+ this.flags = flags2;
11343
11348
  if (unicodeSets && this.parser.options.ecmaVersion >= 15) {
11344
11349
  this.switchU = true;
11345
11350
  this.switchV = true;
@@ -11416,15 +11421,15 @@ RegExpValidationState.prototype.eatChars = function eatChars(chs, forceU) {
11416
11421
  };
11417
11422
  pp$1.validateRegExpFlags = function(state) {
11418
11423
  var validFlags = state.validFlags;
11419
- var flags = state.flags;
11424
+ var flags2 = state.flags;
11420
11425
  var u = false;
11421
11426
  var v = false;
11422
- for (var i2 = 0; i2 < flags.length; i2++) {
11423
- var flag = flags.charAt(i2);
11427
+ for (var i2 = 0; i2 < flags2.length; i2++) {
11428
+ var flag = flags2.charAt(i2);
11424
11429
  if (validFlags.indexOf(flag) === -1) {
11425
11430
  this.raise(state.start, "Invalid regular expression flag");
11426
11431
  }
11427
- if (flags.indexOf(flag, i2 + 1) > -1) {
11432
+ if (flags2.indexOf(flag, i2 + 1) > -1) {
11428
11433
  this.raise(state.start, "Duplicate regular expression flag");
11429
11434
  }
11430
11435
  if (flag === "u") {
@@ -13036,20 +13041,20 @@ pp.readRegexp = function() {
13036
13041
  var pattern = this.input.slice(start, this.pos);
13037
13042
  ++this.pos;
13038
13043
  var flagsStart = this.pos;
13039
- var flags = this.readWord1();
13044
+ var flags2 = this.readWord1();
13040
13045
  if (this.containsEsc) {
13041
13046
  this.unexpected(flagsStart);
13042
13047
  }
13043
13048
  var state = this.regexpState || (this.regexpState = new RegExpValidationState(this));
13044
- state.reset(start, pattern, flags);
13049
+ state.reset(start, pattern, flags2);
13045
13050
  this.validateRegExpFlags(state);
13046
13051
  this.validateRegExpPattern(state);
13047
13052
  var value = null;
13048
13053
  try {
13049
- value = new RegExp(pattern, flags);
13054
+ value = new RegExp(pattern, flags2);
13050
13055
  } catch (e) {
13051
13056
  }
13052
- return this.finishToken(types$1.regexp, { pattern, flags, value });
13057
+ return this.finishToken(types$1.regexp, { pattern, flags: flags2, value });
13053
13058
  };
13054
13059
  pp.readInt = function(radix, len, maybeLegacyOctalNumericLiteral) {
13055
13060
  var allowSeparators = this.options.ecmaVersion >= 12 && len === void 0;
@@ -13590,8 +13595,8 @@ function typeOfField(field) {
13590
13595
  return field.allowMultipleEntries ? arrayOf(base) : base;
13591
13596
  }
13592
13597
  var SEGMENT = /^(?:\.?\[\s*'((?:[^'\\]|\\.)*)'\s*\]|\.?\[\s*"((?:[^"\\]|\\.)*)"\s*\]|\.?\[\s*(\d+)\s*\]|\.?\[\s*\*\s*\]|\.([^.[\]]+))/;
13593
- function parsePath(path15) {
13594
- let rest = path15.trim();
13598
+ function parsePath(path16) {
13599
+ let rest = path16.trim();
13595
13600
  if (rest.startsWith("$")) {
13596
13601
  rest = rest.slice(1);
13597
13602
  }
@@ -14091,10 +14096,10 @@ function describePlaceholder(placeholder, raw, config, outputs, depth) {
14091
14096
  function untested(nodeId) {
14092
14097
  return `node "${nodeId}" has no test output yet (run: softr workflows test ${nodeId})`;
14093
14098
  }
14094
- function describeOutputRef(nodeId, path15, source, outputs) {
14095
- const segments = parsePath(path15);
14099
+ function describeOutputRef(nodeId, path16, source, outputs) {
14100
+ const segments = parsePath(path16);
14096
14101
  if (!segments) {
14097
- return { type: UNKNOWN, source, verified: false, note: `unsupported path "${path15}"` };
14102
+ return { type: UNKNOWN, source, verified: false, note: `unsupported path "${path16}"` };
14098
14103
  }
14099
14104
  const row = outputs[nodeId];
14100
14105
  if (!row) {
@@ -14575,6 +14580,103 @@ async function deleteDatabase(client, databaseId, force) {
14575
14580
  async function listTables(client, databaseId) {
14576
14581
  return (await tablesRequest(client, "GET", `${DATABASES}/${encodeURIComponent(databaseId)}/tables`)).data;
14577
14582
  }
14583
+ function tablePath(databaseId, tableId) {
14584
+ const tables = `${DATABASES}/${encodeURIComponent(databaseId)}/tables`;
14585
+ return tableId ? `${tables}/${encodeURIComponent(tableId)}` : tables;
14586
+ }
14587
+ async function createTable(client, databaseId, table) {
14588
+ return (await tablesRequest(client, "POST", tablePath(databaseId), table)).data;
14589
+ }
14590
+ async function updateTable(client, databaseId, tableId, table) {
14591
+ return (await tablesRequest(client, "PUT", tablePath(databaseId, tableId), table)).data;
14592
+ }
14593
+ async function deleteTable(client, databaseId, tableId, force) {
14594
+ await tablesRequestVoid(client, "DELETE", `${tablePath(databaseId, tableId)}${force ? "?force=true" : ""}`);
14595
+ }
14596
+ async function addField(client, databaseId, tableId, field) {
14597
+ return (await tablesRequest(client, "POST", `${tablePath(databaseId, tableId)}/fields`, field)).data;
14598
+ }
14599
+ async function updateField(client, databaseId, tableId, fieldId, field) {
14600
+ return (await tablesRequest(
14601
+ client,
14602
+ "PUT",
14603
+ `${tablePath(databaseId, tableId)}/fields/${encodeURIComponent(fieldId)}`,
14604
+ field
14605
+ )).data;
14606
+ }
14607
+ async function deleteField(client, databaseId, tableId, fieldId) {
14608
+ await tablesRequestVoid(client, "DELETE", `${tablePath(databaseId, tableId)}/fields/${encodeURIComponent(fieldId)}`);
14609
+ }
14610
+ async function listViews(client, databaseId, tableId) {
14611
+ return (await tablesRequest(client, "GET", `${tablePath(databaseId, tableId)}/views`)).data;
14612
+ }
14613
+ function recordsPath(databaseId, tableId, recordId) {
14614
+ const records = `${tablePath(databaseId, tableId)}/records`;
14615
+ return recordId ? `${records}/${encodeURIComponent(recordId)}` : records;
14616
+ }
14617
+ function names(fieldNames) {
14618
+ return fieldNames ? "fieldNames=true" : "fieldNames=false";
14619
+ }
14620
+ async function listRecords(client, databaseId, tableId, query) {
14621
+ const params = new URLSearchParams({ offset: String(query.offset), limit: String(query.limit) });
14622
+ if (query.viewId) {
14623
+ params.set("viewId", query.viewId);
14624
+ }
14625
+ const response = await tablesRequest(
14626
+ client,
14627
+ "GET",
14628
+ `${recordsPath(databaseId, tableId)}?${params.toString()}&${names(query.fieldNames)}`
14629
+ );
14630
+ return { records: response.data, metadata: response.metadata };
14631
+ }
14632
+ async function searchRecords(client, databaseId, tableId, search, fieldNames) {
14633
+ const response = await tablesRequest(
14634
+ client,
14635
+ "POST",
14636
+ `${recordsPath(databaseId, tableId)}/search?${names(fieldNames)}`,
14637
+ search
14638
+ );
14639
+ return { records: response.data, metadata: response.metadata };
14640
+ }
14641
+ async function getRecord(client, databaseId, tableId, recordId, fieldNames) {
14642
+ return (await tablesRequest(
14643
+ client,
14644
+ "GET",
14645
+ `${recordsPath(databaseId, tableId, recordId)}?${names(fieldNames)}`
14646
+ )).data;
14647
+ }
14648
+ async function createRecord(client, databaseId, tableId, fields, fieldNames) {
14649
+ return (await tablesRequest(
14650
+ client,
14651
+ "POST",
14652
+ `${recordsPath(databaseId, tableId)}?${names(fieldNames)}`,
14653
+ {
14654
+ fields
14655
+ }
14656
+ )).data;
14657
+ }
14658
+ async function updateRecord(client, databaseId, tableId, recordId, fields, fieldNames) {
14659
+ return (await tablesRequest(
14660
+ client,
14661
+ "PATCH",
14662
+ `${recordsPath(databaseId, tableId, recordId)}?${names(fieldNames)}`,
14663
+ { fields }
14664
+ )).data;
14665
+ }
14666
+ async function deleteRecord(client, databaseId, tableId, recordId) {
14667
+ await tablesRequestVoid(client, "DELETE", recordsPath(databaseId, tableId, recordId));
14668
+ }
14669
+ async function uploadAttachment(client, databaseId, tableId, recordId, fieldId, file) {
14670
+ const form = new FormData();
14671
+ form.set("file", new Blob([file.content]), file.name);
14672
+ form.set("fileName", file.name);
14673
+ return (await tablesRequest(
14674
+ client,
14675
+ "PUT",
14676
+ `${recordsPath(databaseId, tableId, recordId)}/fields/${encodeURIComponent(fieldId)}/upload-attachment`,
14677
+ form
14678
+ )).data;
14679
+ }
14578
14680
 
14579
14681
  // src/config/credentials.ts
14580
14682
  import { chmod, mkdir as mkdir4, readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
@@ -14612,8 +14714,8 @@ function refreshFailureHint(status) {
14612
14714
  return status === 401 ? "The OAuth grant is gone (revoked or older than 30 days). Run: softr login" : "The studio could not renew the login right now; try again in a moment.";
14613
14715
  }
14614
14716
  async function tokenRequest(target, form) {
14615
- const path15 = "/v1/oauth/token";
14616
- const response = await fetch(`${target.studioApiUrl}${path15}`, {
14717
+ const path16 = "/v1/oauth/token";
14718
+ const response = await fetch(`${target.studioApiUrl}${path16}`, {
14617
14719
  method: "POST",
14618
14720
  headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" },
14619
14721
  body: new URLSearchParams({ ...form, client_id: oauthClientId() }).toString()
@@ -14621,7 +14723,7 @@ async function tokenRequest(target, form) {
14621
14723
  if (!response.ok) {
14622
14724
  const text = await response.text();
14623
14725
  throw new SoftrApiError(
14624
- `POST ${path15} failed with status ${response.status}${text ? `: ${text}` : ""}`,
14726
+ `POST ${path16} failed with status ${response.status}${text ? `: ${text}` : ""}`,
14625
14727
  response.status,
14626
14728
  text,
14627
14729
  form["grant_type"] === "refresh_token" ? refreshFailureHint(response.status) : void 0
@@ -14791,8 +14893,26 @@ async function authenticatedClient(target) {
14791
14893
  }
14792
14894
 
14793
14895
  // src/project/db-dir.ts
14794
- import { mkdir as mkdir5, readFile as readFile6, rm, writeFile as writeFile6 } from "node:fs/promises";
14896
+ import { mkdir as mkdir5, readdir as readdir2, readFile as readFile6, rm, writeFile as writeFile6 } from "node:fs/promises";
14795
14897
  import path8 from "node:path";
14898
+
14899
+ // src/project/lookup.ts
14900
+ function byIdOrName(items, reference, kind, describe2 = (item) => item.id) {
14901
+ const byId = items.find((item) => item.id === reference);
14902
+ if (byId) {
14903
+ return byId;
14904
+ }
14905
+ const byName = items.filter((item) => item.name === reference);
14906
+ if (byName.length > 1) {
14907
+ throw new CliError(
14908
+ `Several ${kind}s are named "${reference}".`,
14909
+ `Pass the id of one: ${byName.map(describe2).join(", ")}`
14910
+ );
14911
+ }
14912
+ return byName[0];
14913
+ }
14914
+
14915
+ // src/project/db-dir.ts
14796
14916
  var DB_CONFIG_FILE_NAME = "softr-db.jsonc";
14797
14917
  var DB_CACHE_DIR_NAME = ".softr-db";
14798
14918
  var DATABASE_ID_ENV_VAR = "SOFTR_DATABASE_ID";
@@ -14841,8 +14961,41 @@ async function writeSchemaCache(dir, database, tables) {
14841
14961
  const cache = { database, tables, refreshedAt: (/* @__PURE__ */ new Date()).toISOString() };
14842
14962
  await mkdir5(path8.dirname(cacheFile(dir)), { recursive: true });
14843
14963
  await writeFile6(cacheFile(dir), JSON.stringify(cache, null, 2) + "\n");
14964
+ await pruneViewsCache(dir, new Set(tables.map((table) => table.id)));
14844
14965
  return cache;
14845
14966
  }
14967
+ function viewsDir(dir) {
14968
+ return path8.join(dir, DB_CACHE_DIR_NAME, "views");
14969
+ }
14970
+ async function readViewsCache(dir, tableId) {
14971
+ try {
14972
+ return JSON.parse(await readFile6(path8.join(viewsDir(dir), `${tableId}.json`), "utf8"));
14973
+ } catch (error) {
14974
+ if (error.code === "ENOENT") {
14975
+ return void 0;
14976
+ }
14977
+ throw error;
14978
+ }
14979
+ }
14980
+ async function writeViewsCache(dir, tableId, views) {
14981
+ const value = { tableId, views, refreshedAt: (/* @__PURE__ */ new Date()).toISOString() };
14982
+ await mkdir5(viewsDir(dir), { recursive: true });
14983
+ await writeFile6(path8.join(viewsDir(dir), `${tableId}.json`), JSON.stringify(value, null, 2) + "\n");
14984
+ }
14985
+ async function pruneViewsCache(dir, tableIds) {
14986
+ let names2;
14987
+ try {
14988
+ names2 = await readdir2(viewsDir(dir));
14989
+ } catch (error) {
14990
+ if (error.code === "ENOENT") {
14991
+ return;
14992
+ }
14993
+ throw error;
14994
+ }
14995
+ await Promise.all(
14996
+ names2.filter((name) => name.endsWith(".json") && !tableIds.has(name.slice(0, -".json".length))).map((name) => rm(path8.join(viewsDir(dir), name), { force: true }))
14997
+ );
14998
+ }
14846
14999
  async function refreshSchemaCache(client, dir, databaseId) {
14847
15000
  const [database, tables] = await Promise.all([getDatabase(client, databaseId), listTables(client, databaseId)]);
14848
15001
  return writeSchemaCache(dir, database, tables);
@@ -14903,25 +15056,19 @@ async function databaseDirFor(cwd, explicitDir, databaseId, dirName) {
14903
15056
  return { dir: sibling2, existing: false };
14904
15057
  }
14905
15058
  async function findReachableDatabase(client, reference) {
14906
- const reachable = await listDatabases(client);
14907
- const byId = reachable.find((database) => database.id === reference);
14908
- if (byId) {
14909
- return byId;
14910
- }
14911
- const byName = reachable.filter((database) => database.name === reference);
14912
- if (byName.length > 1) {
14913
- throw new CliError(
14914
- `Several databases are named "${reference}".`,
14915
- `Pass the id of one: ${byName.map((database) => `${database.id} (workspace ${database.workspaceId})`).join(", ")}`
14916
- );
14917
- }
14918
- if (!byName[0]) {
15059
+ const database = byIdOrName(
15060
+ await listDatabases(client),
15061
+ reference,
15062
+ "database",
15063
+ (item) => `${item.id} (workspace ${item.workspaceId})`
15064
+ );
15065
+ if (!database) {
14919
15066
  throw new CliError(
14920
15067
  `The token reaches no database with the id or name "${reference}".`,
14921
15068
  '"softr db list" shows the ones it reaches.'
14922
15069
  );
14923
15070
  }
14924
- return byName[0];
15071
+ return database;
14925
15072
  }
14926
15073
  async function bindDatabaseDir(client, cwd, reference, explicitDir) {
14927
15074
  const here = path8.resolve(cwd, explicitDir ?? ".");
@@ -14944,6 +15091,10 @@ async function bindDatabaseDir(client, cwd, reference, explicitDir) {
14944
15091
  return { databaseId: database.id, dir, wrote: !existing };
14945
15092
  }
14946
15093
 
15094
+ // src/commands/db/help.ts
15095
+ var DATABASE_ID_HELP = `the database id (default: $SOFTR_DATABASE_ID, else the one in ${DB_CONFIG_FILE_NAME})`;
15096
+ var DATABASE_DIR_HELP = `database directory, the one holding ${DB_CONFIG_FILE_NAME} (default: the current directory)`;
15097
+
14947
15098
  // src/api/studio.ts
14948
15099
  async function resolveTokenOwner(client) {
14949
15100
  try {
@@ -15420,18 +15571,11 @@ Next: softr workflows add <TYPE> --dir ${path10.relative(cwd, dir) || "."} (see
15420
15571
  }
15421
15572
 
15422
15573
  // src/commands/db/databases.ts
15423
- var DATABASE_ARGUMENT = `the database id (default: $SOFTR_DATABASE_ID, else the one in ${DB_CONFIG_FILE_NAME})`;
15424
- var DIR_OPTION = `database directory, the one holding ${DB_CONFIG_FILE_NAME} (default: the current directory)`;
15425
15574
  function renderDatabases(databases) {
15426
15575
  if (!databases.length) {
15427
15576
  return "The token reaches no database. Create one with: softr db create <name>";
15428
15577
  }
15429
- const table = new import_cli_table3.default({
15430
- head: ["ID", "Name", "Workspace", "Tables"],
15431
- colWidths: [38, 34, 38, 8],
15432
- wordWrap: true,
15433
- style: { head: [], border: [], "padding-left": 1, "padding-right": 1 }
15434
- });
15578
+ const table = new import_cli_table3.default(plainTable(["ID", "Name", "Workspace", "Tables"], [38, 34, 38, 8]));
15435
15579
  for (const database of databases) {
15436
15580
  table.push([database.id, database.name, database.workspaceId, String(database.tablesCount)]);
15437
15581
  }
@@ -15502,7 +15646,7 @@ Wrote ${path11.join(relativeDir(cwd, dir), DB_CONFIG_FILE_NAME)}.` : "";
15502
15646
  );
15503
15647
  }
15504
15648
  );
15505
- db2.command("update").description("Change a database's name or description").argument("[databaseId]", DATABASE_ARGUMENT).option("--name <name>", "the new name").option("--description <text>", "the new description").option("--dir <path>", DIR_OPTION).action(
15649
+ db2.command("update").description("Change a database's name or description").argument("[databaseId]", DATABASE_ID_HELP).option("--name <name>", "the new name").option("--description <text>", "the new description").option("--dir <path>", DATABASE_DIR_HELP).action(
15506
15650
  async (databaseId, opts, command) => {
15507
15651
  if (!opts.name && !opts.description) {
15508
15652
  throw new CliError("Nothing to change: pass --name, --description or both.");
@@ -15521,7 +15665,7 @@ Wrote ${path11.join(relativeDir(cwd, dir), DB_CONFIG_FILE_NAME)}.` : "";
15521
15665
  emit(options, `Updated database ${database.name} (${database.id}).`, { database });
15522
15666
  }
15523
15667
  );
15524
- db2.command("delete").description("Delete a database; one that still has tables needs --force").argument("[databaseId]", DATABASE_ARGUMENT).option("--force", "also delete its tables and all their records").option("--dir <path>", DIR_OPTION).action(async (databaseId, opts, command) => {
15668
+ db2.command("delete").description("Delete a database; one that still has tables needs --force").argument("[databaseId]", DATABASE_ID_HELP).option("--force", "also delete its tables and all their records").option("--dir <path>", DATABASE_DIR_HELP).action(async (databaseId, opts, command) => {
15525
15669
  const options = globalOptions(command);
15526
15670
  const target = await resolveDatabase(process.cwd(), databaseId, opts.dir);
15527
15671
  const client = await authenticatedClient(resolveTarget());
@@ -15538,9 +15682,637 @@ ${path11.join(relativeDir(process.cwd(), target.dir), DB_CONFIG_FILE_NAME)} stil
15538
15682
  });
15539
15683
  }
15540
15684
 
15541
- // src/commands/integrations.ts
15542
- var import_cli_table32 = __toESM(require_cli_table3(), 1);
15685
+ // src/commands/db/records.ts
15686
+ import { readFile as readFile9 } from "node:fs/promises";
15543
15687
  import path12 from "node:path";
15688
+
15689
+ // src/commands/db/json-input.ts
15690
+ import { readFile as readFile8 } from "node:fs/promises";
15691
+ async function readJsonArgument(value, option, hint) {
15692
+ let text = value;
15693
+ if (value === "-") {
15694
+ const chunks = [];
15695
+ for await (const chunk of process.stdin) {
15696
+ chunks.push(chunk);
15697
+ }
15698
+ text = Buffer.concat(chunks).toString("utf8");
15699
+ } else if (value.startsWith("@")) {
15700
+ text = await readFile8(value.slice(1), "utf8");
15701
+ }
15702
+ try {
15703
+ return JSON.parse(text);
15704
+ } catch {
15705
+ throw new CliError(`${option} is not valid JSON.`, hint ?? "Pass inline JSON, @<file> or - for stdin.");
15706
+ }
15707
+ }
15708
+
15709
+ // src/commands/db/schema.ts
15710
+ var import_cli_table32 = __toESM(require_cli_table3(), 1);
15711
+ var FIELD_REFERENCE_URL = "https://docs.softr.io/softr-api/softr-database-api/table-fields/add-table-field";
15712
+ var TABLE_ARGUMENT = "the table id or exact name";
15713
+ var FIELD_ARGUMENT = "the field id or exact name";
15714
+ function withDatabaseOptions(command) {
15715
+ return command.option("--database <id>", DATABASE_ID_HELP).option("--dir <path>", DATABASE_DIR_HELP);
15716
+ }
15717
+ async function schemaContext(opts) {
15718
+ const target = await resolveDatabase(process.cwd(), opts.database, opts.dir);
15719
+ const client = await authenticatedClient(resolveTarget());
15720
+ const cache = target.dir ? await readSchemaCache(target.dir) : void 0;
15721
+ return { client, databaseId: target.databaseId, dir: target.dir, cache };
15722
+ }
15723
+ async function currentTables(context, fresh) {
15724
+ if (!context.dir) {
15725
+ return listTables(context.client, context.databaseId);
15726
+ }
15727
+ if (!fresh && context.cache) {
15728
+ return context.cache.tables;
15729
+ }
15730
+ context.cache = await refreshSchemaCache(context.client, context.dir, context.databaseId);
15731
+ return context.cache.tables;
15732
+ }
15733
+ async function findTable(context, reference, fresh = false) {
15734
+ const table = byIdOrName(await currentTables(context, fresh), reference, "table") ?? (context.dir && !fresh ? byIdOrName(await currentTables(context, true), reference, "table") : void 0);
15735
+ if (!table) {
15736
+ throw new CliError(
15737
+ `Database ${context.databaseId} has no table with the id or name "${reference}".`,
15738
+ '"softr db tables list" shows its tables.'
15739
+ );
15740
+ }
15741
+ return table;
15742
+ }
15743
+ async function findFields(context, table, references, fresh = false) {
15744
+ const resolve = (current3) => {
15745
+ const fields2 = /* @__PURE__ */ new Map();
15746
+ for (const reference of references) {
15747
+ const field = byIdOrName(current3.fields, reference, "field");
15748
+ if (field) {
15749
+ fields2.set(reference, field);
15750
+ }
15751
+ }
15752
+ return fields2;
15753
+ };
15754
+ let current2 = table;
15755
+ let fields = resolve(current2);
15756
+ if (fields.size < new Set(references).size && context.dir && !fresh) {
15757
+ current2 = await findTable(context, table.id, true);
15758
+ fields = resolve(current2);
15759
+ }
15760
+ const missing = references.filter((reference) => !fields.has(reference));
15761
+ if (missing.length) {
15762
+ throw new CliError(
15763
+ `Table ${current2.name} has no field with the id or name ${missing.map((name) => `"${name}"`).join(", ")}.`,
15764
+ `"softr db tables get ${current2.id}" shows its fields.`
15765
+ );
15766
+ }
15767
+ return { table: current2, fields };
15768
+ }
15769
+ async function findField(context, tableReference, reference, fresh = false) {
15770
+ const found = await findFields(context, await findTable(context, tableReference, fresh), [reference], fresh);
15771
+ return { table: found.table, field: found.fields.get(reference) };
15772
+ }
15773
+ async function afterSchemaWrite(context) {
15774
+ if (context.dir) {
15775
+ context.cache = await refreshSchemaCache(context.client, context.dir, context.databaseId);
15776
+ }
15777
+ }
15778
+ function parseOptions(value) {
15779
+ return value === void 0 ? Promise.resolve(void 0) : readJsonArgument(value, "--options", `The options of each field type: ${FIELD_REFERENCE_URL}`);
15780
+ }
15781
+ function parseFieldSpec(spec) {
15782
+ const colon = spec.lastIndexOf(":");
15783
+ const name = colon > 0 ? spec.slice(0, colon).trim() : "";
15784
+ const type = colon > 0 ? spec.slice(colon + 1).trim().toUpperCase() : "";
15785
+ if (!name || !type) {
15786
+ throw new CliError(`--field "${spec}" must be <name>:<TYPE>, for example "Due date:DATE".`);
15787
+ }
15788
+ return { name, type };
15789
+ }
15790
+ function renderTables(tables) {
15791
+ if (!tables.length) {
15792
+ return "The database has no tables. Create one with: softr db tables create <name>";
15793
+ }
15794
+ const table = new import_cli_table32.default(plainTable(["ID", "Name", "Fields", "Primary field"], [18, 34, 8, 30]));
15795
+ for (const item of tables) {
15796
+ const primary = item.fields.find((field) => field.id === item.primaryFieldId);
15797
+ table.push([item.id, item.name, String(item.fields.length), primary?.name ?? item.primaryFieldId]);
15798
+ }
15799
+ return table.toString();
15800
+ }
15801
+ function flags(field, primaryFieldId) {
15802
+ return [
15803
+ field.id === primaryFieldId ? "primary" : "",
15804
+ field.required ? "required" : "",
15805
+ field.readonly ? "read-only" : "",
15806
+ field.allowMultipleEntries ? "multiple" : ""
15807
+ ].filter(Boolean).join(", ");
15808
+ }
15809
+ function renderTable(item) {
15810
+ const fields = new import_cli_table32.default(plainTable(["ID", "Name", "Type", ""], [18, 30, 18, 30]));
15811
+ for (const field of item.fields) {
15812
+ fields.push([field.id, field.name, field.type, flags(field, item.primaryFieldId)]);
15813
+ }
15814
+ return [
15815
+ `${item.name} (${item.id})`,
15816
+ ...item.description ? [` ${item.description}`] : [],
15817
+ ` Default view: ${item.defaultViewId}`,
15818
+ fields.toString()
15819
+ ].join("\n");
15820
+ }
15821
+ function renderField(table, field) {
15822
+ return [
15823
+ `${field.name} (${field.id}) in ${table.name}`,
15824
+ ...field.description ? [` ${field.description}`] : [],
15825
+ ` Type: ${field.type}`,
15826
+ ...flags(field, table.primaryFieldId) ? [` Flags: ${flags(field, table.primaryFieldId)}`] : [],
15827
+ ` Options: ${JSON.stringify(field.options ?? null)}`
15828
+ ].join("\n");
15829
+ }
15830
+ function renderViews(views) {
15831
+ const table = new import_cli_table32.default(plainTable(["ID", "Name"], [18, 50]));
15832
+ for (const view of views) {
15833
+ table.push([view.id, view.name]);
15834
+ }
15835
+ return table.toString();
15836
+ }
15837
+ function registerTables(db2) {
15838
+ const tables = db2.command("tables").description("The tables of a database, with their fields");
15839
+ withDatabaseOptions(tables.command("list").description("List the tables of the database")).action(
15840
+ async (opts, command) => {
15841
+ const options = globalOptions(command);
15842
+ const context = await schemaContext(opts);
15843
+ const items = await currentTables(context, true);
15844
+ emit(options, renderTables(items), { databaseId: context.databaseId, tables: items });
15845
+ }
15846
+ );
15847
+ withDatabaseOptions(
15848
+ tables.command("get").description("Show a table with its fields").argument("<table>", TABLE_ARGUMENT)
15849
+ ).action(async (reference, opts, command) => {
15850
+ const options = globalOptions(command);
15851
+ const context = await schemaContext(opts);
15852
+ const table = await findTable(context, reference, true);
15853
+ emit(options, renderTable(table), { table });
15854
+ });
15855
+ withDatabaseOptions(
15856
+ tables.command("create").description(
15857
+ "Create a table with at least one field; the first one names a record unless --primary-field says otherwise"
15858
+ ).argument("<name>", "the table name").option("--description <text>", "what the table holds").option(
15859
+ "--field <name:TYPE>",
15860
+ `a field to create with it, repeatable (types and their options: ${FIELD_REFERENCE_URL}); add fields with options through "softr db fields add"`,
15861
+ (value, previous) => [...previous, value],
15862
+ []
15863
+ ).option("--primary-field <name>", "the field that names a record (default: the first field)")
15864
+ ).action(
15865
+ async (name, opts, command) => {
15866
+ const options = globalOptions(command);
15867
+ if (!opts.field.length) {
15868
+ throw new CliError(
15869
+ 'A table needs at least one field: pass --field <name>:<TYPE>, for example --field "Name:SINGLE_LINE_TEXT".',
15870
+ "Repeat --field for more; the first one names a record unless --primary-field names another."
15871
+ );
15872
+ }
15873
+ const fields = opts.field.map(parseFieldSpec);
15874
+ if (opts.primaryField && !fields.some((field) => field.name === opts.primaryField)) {
15875
+ throw new CliError(`--primary-field "${opts.primaryField}" is not one of the --field names.`);
15876
+ }
15877
+ const context = await schemaContext(opts);
15878
+ const table = await createTable(context.client, context.databaseId, {
15879
+ name,
15880
+ description: opts.description,
15881
+ primaryFieldName: opts.primaryField ?? fields[0].name,
15882
+ fields
15883
+ });
15884
+ await afterSchemaWrite(context);
15885
+ emit(options, `Created table ${table.name} (${table.id}) with ${table.fields.length} fields.`, { table });
15886
+ }
15887
+ );
15888
+ withDatabaseOptions(
15889
+ tables.command("update").description("Change a table's name or description").argument("<table>", TABLE_ARGUMENT).option("--name <name>", "the new name").option("--description <text>", "the new description")
15890
+ ).action(
15891
+ async (reference, opts, command) => {
15892
+ if (!opts.name && opts.description === void 0) {
15893
+ throw new CliError("Nothing to change: pass --name, --description or both.");
15894
+ }
15895
+ const options = globalOptions(command);
15896
+ const context = await schemaContext(opts);
15897
+ const current2 = await findTable(context, reference, true);
15898
+ const table = await updateTable(context.client, context.databaseId, current2.id, {
15899
+ name: opts.name ?? current2.name,
15900
+ description: opts.description ?? current2.description
15901
+ });
15902
+ await afterSchemaWrite(context);
15903
+ emit(options, `Updated table ${table.name} (${table.id}).`, { table });
15904
+ }
15905
+ );
15906
+ withDatabaseOptions(
15907
+ tables.command("delete").description("Delete a table; one that still has records needs --force").argument("<table>", TABLE_ARGUMENT).option("--force", "also delete all its records")
15908
+ ).action(async (reference, opts, command) => {
15909
+ const options = globalOptions(command);
15910
+ const context = await schemaContext(opts);
15911
+ const table = await findTable(context, reference, true);
15912
+ await deleteTable(context.client, context.databaseId, table.id, opts.force === true);
15913
+ await afterSchemaWrite(context);
15914
+ emit(options, `Deleted table ${table.name} (${table.id}).`, { deleted: table.id });
15915
+ });
15916
+ }
15917
+ function registerFields(db2) {
15918
+ const fields = db2.command("fields").description("The fields of a table");
15919
+ withDatabaseOptions(
15920
+ fields.command("get").description("Show a field with its options").argument("<table>", TABLE_ARGUMENT).argument("<field>", FIELD_ARGUMENT)
15921
+ ).action(async (tableReference, reference, opts, command) => {
15922
+ const options = globalOptions(command);
15923
+ const context = await schemaContext(opts);
15924
+ const { table, field } = await findField(context, tableReference, reference, true);
15925
+ emit(options, renderField(table, field), { tableId: table.id, field });
15926
+ });
15927
+ withDatabaseOptions(
15928
+ fields.command("add").description("Add a field to a table").argument("<table>", TABLE_ARGUMENT).argument("<name>", "the field name").requiredOption("--type <TYPE>", "the field type, for example SINGLE_LINE_TEXT, NUMBER, SELECT, LINKED_RECORD").option("--options <json>", `the type's options as JSON, @<file> or - for stdin (${FIELD_REFERENCE_URL})`).option("--description <text>", "what the field holds").option("--multiple", "allow several values (SELECT, LINKED_RECORD, USER, ATTACHMENT)")
15929
+ ).action(
15930
+ async (tableReference, name, opts, command) => {
15931
+ const options = globalOptions(command);
15932
+ const fieldOptions = await parseOptions(opts.options);
15933
+ const context = await schemaContext(opts);
15934
+ const table = await findTable(context, tableReference);
15935
+ const field = await addField(context.client, context.databaseId, table.id, {
15936
+ name,
15937
+ type: opts.type.toUpperCase(),
15938
+ description: opts.description,
15939
+ ...fieldOptions === void 0 ? {} : { options: fieldOptions },
15940
+ allowMultipleEntries: opts.multiple === true
15941
+ });
15942
+ await afterSchemaWrite(context);
15943
+ emit(options, `Added field ${field.name} (${field.id}, ${field.type}) to ${table.name}.`, {
15944
+ tableId: table.id,
15945
+ field
15946
+ });
15947
+ }
15948
+ );
15949
+ withDatabaseOptions(
15950
+ fields.command("update").description("Change a field: what is not passed stays as it is").argument("<table>", TABLE_ARGUMENT).argument("<field>", FIELD_ARGUMENT).option("--name <name>", "the new name").option("--type <TYPE>", "the new type").option(
15951
+ "--options <json>",
15952
+ `the new options as JSON, @<file> or - for stdin, replacing the current ones (${FIELD_REFERENCE_URL})`
15953
+ ).option("--description <text>", "the new description").option("--multiple", "allow several values").option("--no-multiple", "allow one value only")
15954
+ ).action(
15955
+ async (tableReference, reference, opts, command) => {
15956
+ const fieldOptions = await parseOptions(opts.options);
15957
+ if (!opts.name && !opts.type && fieldOptions === void 0 && opts.description === void 0 && opts.multiple === void 0) {
15958
+ throw new CliError("Nothing to change: pass --name, --type, --options, --description or --[no-]multiple.");
15959
+ }
15960
+ const options = globalOptions(command);
15961
+ const context = await schemaContext(opts);
15962
+ const { table, field: current2 } = await findField(context, tableReference, reference, true);
15963
+ const field = await updateField(context.client, context.databaseId, table.id, current2.id, {
15964
+ name: opts.name ?? current2.name,
15965
+ type: opts.type?.toUpperCase() ?? current2.type,
15966
+ description: opts.description ?? current2.description,
15967
+ options: fieldOptions ?? current2.options,
15968
+ aiOptions: current2.aiOptions ?? null,
15969
+ defaultValue: current2.defaultValue ?? null,
15970
+ allowMultipleEntries: opts.multiple ?? current2.allowMultipleEntries
15971
+ });
15972
+ await afterSchemaWrite(context);
15973
+ emit(options, `Updated field ${field.name} (${field.id}, ${field.type}) in ${table.name}.`, {
15974
+ tableId: table.id,
15975
+ field
15976
+ });
15977
+ }
15978
+ );
15979
+ withDatabaseOptions(
15980
+ fields.command("delete").description("Delete a field and its values in every record").argument("<table>", TABLE_ARGUMENT).argument("<field>", FIELD_ARGUMENT)
15981
+ ).action(async (tableReference, reference, opts, command) => {
15982
+ const options = globalOptions(command);
15983
+ const context = await schemaContext(opts);
15984
+ const { table, field } = await findField(context, tableReference, reference, true);
15985
+ await deleteField(context.client, context.databaseId, table.id, field.id);
15986
+ await afterSchemaWrite(context);
15987
+ emit(options, `Deleted field ${field.name} (${field.id}) from ${table.name}.`, {
15988
+ tableId: table.id,
15989
+ deleted: field.id
15990
+ });
15991
+ });
15992
+ }
15993
+ function registerViews(db2) {
15994
+ const views = db2.command("views").description("The views of a table");
15995
+ withDatabaseOptions(
15996
+ views.command("list").description("List the views of a table").argument("<table>", TABLE_ARGUMENT)
15997
+ ).action(async (reference, opts, command) => {
15998
+ const options = globalOptions(command);
15999
+ const context = await schemaContext(opts);
16000
+ const table = await findTable(context, reference, true);
16001
+ const items = await listViews(context.client, context.databaseId, table.id);
16002
+ if (context.dir) {
16003
+ await writeViewsCache(context.dir, table.id, items);
16004
+ }
16005
+ emit(options, renderViews(items), { tableId: table.id, views: items });
16006
+ });
16007
+ }
16008
+
16009
+ // src/commands/db/records.ts
16010
+ var SEARCH_DOCS_URL = "https://docs.softr.io/softr-api/softr-database-api/records/search-records";
16011
+ var MAX_PAGE = 200;
16012
+ function withOutputOptions(command) {
16013
+ return command.option("--field-ids", "key record fields by field id instead of field name");
16014
+ }
16015
+ function withPagingOptions(command) {
16016
+ return command.option("--limit <n>", `records per page, 1 to ${MAX_PAGE} (default 20)`).option("--offset <n>", "records to skip (default 0)").option("--all", `every record, read in pages of ${MAX_PAGE}`);
16017
+ }
16018
+ function parseCount(value, option, max) {
16019
+ if (value === void 0) {
16020
+ return void 0;
16021
+ }
16022
+ const count = Number(value);
16023
+ if (!Number.isInteger(count) || count < 0 || max !== void 0 && (count < 1 || count > max)) {
16024
+ throw new CliError(`${option} must be a whole number${max ? ` from 1 to ${max}` : " of 0 or more"}.`);
16025
+ }
16026
+ return count;
16027
+ }
16028
+ function paging(opts) {
16029
+ if (opts.all && opts.limit !== void 0) {
16030
+ throw new CliError("Pass --all or --limit, not both.");
16031
+ }
16032
+ return {
16033
+ offset: parseCount(opts.offset, "--offset") ?? 0,
16034
+ limit: opts.all ? MAX_PAGE : parseCount(opts.limit, "--limit", MAX_PAGE) ?? 20,
16035
+ all: opts.all === true
16036
+ };
16037
+ }
16038
+ async function readPages(first, read) {
16039
+ const page = await read(first.offset, first.limit);
16040
+ if (!first.all) {
16041
+ return page;
16042
+ }
16043
+ const records = [...page.records];
16044
+ let offset2 = first.offset + page.records.length;
16045
+ while (page.records.length && offset2 < page.metadata.total) {
16046
+ const next = await read(offset2, first.limit);
16047
+ if (!next.records.length) {
16048
+ break;
16049
+ }
16050
+ records.push(...next.records);
16051
+ offset2 += next.records.length;
16052
+ }
16053
+ return { records, metadata: { offset: first.offset, limit: records.length, total: page.metadata.total } };
16054
+ }
16055
+ function asObject(value, option) {
16056
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
16057
+ throw new CliError(`${option} must be a JSON object.`);
16058
+ }
16059
+ return value;
16060
+ }
16061
+ async function fieldIdsFor(context, table, references) {
16062
+ const found = await findFields(context, table, references);
16063
+ return { table: found.table, ids: new Map([...found.fields].map(([reference, field]) => [reference, field.id])) };
16064
+ }
16065
+ async function mapFieldKeys(context, table, fields) {
16066
+ const { ids } = await fieldIdsFor(context, table, Object.keys(fields));
16067
+ return Object.fromEntries(Object.entries(fields).map(([key, value]) => [ids.get(key), value]));
16068
+ }
16069
+ function fieldReferences(condition, found) {
16070
+ if (Array.isArray(condition)) {
16071
+ condition.forEach((item) => fieldReferences(item, found));
16072
+ } else if (condition && typeof condition === "object") {
16073
+ for (const [key, value] of Object.entries(condition)) {
16074
+ if (key === "leftSide" && typeof value === "string") {
16075
+ found.add(value);
16076
+ } else {
16077
+ fieldReferences(value, found);
16078
+ }
16079
+ }
16080
+ }
16081
+ }
16082
+ function replaceFieldReferences(condition, ids) {
16083
+ if (Array.isArray(condition)) {
16084
+ return condition.map((item) => replaceFieldReferences(item, ids));
16085
+ }
16086
+ if (condition && typeof condition === "object") {
16087
+ return Object.fromEntries(
16088
+ Object.entries(condition).map(([key, value]) => [
16089
+ key,
16090
+ key === "leftSide" && typeof value === "string" ? ids.get(value) ?? value : replaceFieldReferences(value, ids)
16091
+ ])
16092
+ );
16093
+ }
16094
+ return condition;
16095
+ }
16096
+ function parseSort(spec) {
16097
+ const colon = spec.lastIndexOf(":");
16098
+ const direction = colon > 0 ? spec.slice(colon + 1).trim().toUpperCase() : "";
16099
+ if (direction === "ASC" || direction === "DESC") {
16100
+ return { field: spec.slice(0, colon).trim(), sortType: direction };
16101
+ }
16102
+ return { field: spec.trim(), sortType: "ASC" };
16103
+ }
16104
+ async function findView(context, table, reference) {
16105
+ const fresh = async () => {
16106
+ const views = await listViews(context.client, context.databaseId, table.id);
16107
+ if (context.dir) {
16108
+ await writeViewsCache(context.dir, table.id, views);
16109
+ }
16110
+ return views;
16111
+ };
16112
+ const cached = context.dir ? (await readViewsCache(context.dir, table.id))?.views : void 0;
16113
+ const view = (cached ? byIdOrName(cached, reference, "view") : void 0) ?? byIdOrName(await fresh(), reference, "view");
16114
+ if (!view) {
16115
+ throw new CliError(
16116
+ `Table ${table.name} has no view with the id or name "${reference}".`,
16117
+ `"softr db views list ${table.id}" shows its views.`
16118
+ );
16119
+ }
16120
+ return view;
16121
+ }
16122
+ function withFieldNames(record, table) {
16123
+ const names2 = new Map(table.fields.map((field) => [field.id, field.name]));
16124
+ return {
16125
+ ...record,
16126
+ fields: Object.fromEntries(Object.entries(record.fields).map(([key, value]) => [names2.get(key) ?? key, value]))
16127
+ };
16128
+ }
16129
+ function formatValue(value) {
16130
+ return typeof value === "string" ? value : JSON.stringify(value);
16131
+ }
16132
+ function renderRecord(record) {
16133
+ return [record.id, ...Object.entries(record.fields).map(([key, value]) => ` ${key}: ${formatValue(value)}`)].join(
16134
+ "\n"
16135
+ );
16136
+ }
16137
+ function renderPage(page) {
16138
+ if (!page.records.length) {
16139
+ return page.metadata.total ? `No records from offset ${page.metadata.offset}; ${page.metadata.total} in all.` : "No records.";
16140
+ }
16141
+ const shown = page.records.length;
16142
+ const end = page.metadata.offset + shown;
16143
+ const footer = end < page.metadata.total ? `${shown} of ${page.metadata.total} records; --offset ${end} for the next ones, or --all.` : `${shown} of ${page.metadata.total} records.`;
16144
+ return [...page.records.map(renderRecord), footer].join("\n\n");
16145
+ }
16146
+ function registerRecords(db2) {
16147
+ const records = db2.command("records").description("The records of a table; fields are keyed by name unless --field-ids");
16148
+ withOutputOptions(
16149
+ withPagingOptions(
16150
+ withDatabaseOptions(
16151
+ records.command("list").description(
16152
+ `List a table's records in the order and with the filter of a view, the table's default view unless --view; "records search" without --filter reads every record`
16153
+ ).argument("<table>", TABLE_ARGUMENT).option("--view <view>", "the view id or exact name (default: the table's default view)")
16154
+ )
16155
+ )
16156
+ ).action(
16157
+ async (reference, opts, command) => {
16158
+ const options = globalOptions(command);
16159
+ const pages = paging(opts);
16160
+ const context = await schemaContext(opts);
16161
+ const table = await findTable(context, reference);
16162
+ const viewId = opts.view ? (await findView(context, table, opts.view)).id : void 0;
16163
+ const page = await readPages(
16164
+ pages,
16165
+ (offset2, limit) => listRecords(context.client, context.databaseId, table.id, {
16166
+ viewId,
16167
+ offset: offset2,
16168
+ limit,
16169
+ fieldNames: !opts.fieldIds
16170
+ })
16171
+ );
16172
+ emit(options, renderPage(page), { tableId: table.id, ...page });
16173
+ }
16174
+ );
16175
+ withOutputOptions(
16176
+ withPagingOptions(
16177
+ withDatabaseOptions(
16178
+ records.command("search").description("Find records by a filter, sorted by fields").argument("<table>", TABLE_ARGUMENT).option(
16179
+ "--filter <json>",
16180
+ `the condition as JSON, @<file> or - for stdin; leftSide takes a field name or id (operators: ${SEARCH_DOCS_URL})`
16181
+ ).option(
16182
+ "--sort <field[:asc|desc]>",
16183
+ "sort by a field, repeatable",
16184
+ (value, previous) => [...previous, value],
16185
+ []
16186
+ )
16187
+ )
16188
+ )
16189
+ ).action(
16190
+ async (reference, opts, command) => {
16191
+ const options = globalOptions(command);
16192
+ const pages = paging(opts);
16193
+ const parsed = opts.filter === void 0 ? void 0 : asObject(await readJsonArgument(opts.filter, "--filter"), "--filter");
16194
+ const condition = parsed && "condition" in parsed ? parsed["condition"] : parsed;
16195
+ const sorts = opts.sort.map(parseSort);
16196
+ const context = await schemaContext(opts);
16197
+ const references = new Set(sorts.map((sort) => sort.field));
16198
+ fieldReferences(condition, references);
16199
+ const { table, ids } = await fieldIdsFor(context, await findTable(context, reference), [...references]);
16200
+ const search = (offset2, limit) => ({
16201
+ paging: { offset: offset2, limit },
16202
+ ...sorts.length ? { sorting: sorts.map((sort) => ({ sortingField: ids.get(sort.field), sortType: sort.sortType })) } : {},
16203
+ ...condition === void 0 ? {} : { filter: { condition: replaceFieldReferences(condition, ids) } }
16204
+ });
16205
+ const page = await readPages(
16206
+ pages,
16207
+ (offset2, limit) => searchRecords(context.client, context.databaseId, table.id, search(offset2, limit), !opts.fieldIds)
16208
+ );
16209
+ emit(options, renderPage(page), { tableId: table.id, ...page });
16210
+ }
16211
+ );
16212
+ withOutputOptions(
16213
+ withDatabaseOptions(
16214
+ records.command("get").description("Show one record").argument("<table>", TABLE_ARGUMENT).argument("<recordId>", "the record id")
16215
+ )
16216
+ ).action(async (reference, recordId, opts, command) => {
16217
+ const options = globalOptions(command);
16218
+ const context = await schemaContext(opts);
16219
+ const table = await findTable(context, reference);
16220
+ const record = await getRecord(context.client, context.databaseId, table.id, recordId, !opts.fieldIds);
16221
+ emit(options, renderRecord(record), { tableId: table.id, record });
16222
+ });
16223
+ withOutputOptions(
16224
+ withDatabaseOptions(
16225
+ records.command("create").description("Create a record").argument("<table>", TABLE_ARGUMENT).requiredOption(
16226
+ "--fields <json>",
16227
+ "the values as a JSON object keyed by field name or id, @<file> or - for stdin; {} creates an empty record"
16228
+ )
16229
+ )
16230
+ ).action(async (reference, opts, command) => {
16231
+ const options = globalOptions(command);
16232
+ const fields = asObject(await readJsonArgument(opts.fields, "--fields"), "--fields");
16233
+ const context = await schemaContext(opts);
16234
+ const table = await findTable(context, reference);
16235
+ const record = await createRecord(
16236
+ context.client,
16237
+ context.databaseId,
16238
+ table.id,
16239
+ await mapFieldKeys(context, table, fields),
16240
+ !opts.fieldIds
16241
+ );
16242
+ emit(options, `Created record ${record.id} in ${table.name}.`, { tableId: table.id, record });
16243
+ });
16244
+ withOutputOptions(
16245
+ withDatabaseOptions(
16246
+ records.command("update").description("Change some fields of a record; the others keep their values").argument("<table>", TABLE_ARGUMENT).argument("<recordId>", "the record id").requiredOption(
16247
+ "--fields <json>",
16248
+ "the new values as a JSON object keyed by field name or id, @<file> or - for stdin; null clears a field"
16249
+ )
16250
+ )
16251
+ ).action(
16252
+ async (reference, recordId, opts, command) => {
16253
+ const options = globalOptions(command);
16254
+ const fields = asObject(await readJsonArgument(opts.fields, "--fields"), "--fields");
16255
+ if (!Object.keys(fields).length) {
16256
+ throw new CliError("Nothing to change: --fields is an empty object.");
16257
+ }
16258
+ const context = await schemaContext(opts);
16259
+ const table = await findTable(context, reference);
16260
+ const record = await updateRecord(
16261
+ context.client,
16262
+ context.databaseId,
16263
+ table.id,
16264
+ recordId,
16265
+ await mapFieldKeys(context, table, fields),
16266
+ !opts.fieldIds
16267
+ );
16268
+ emit(options, `Updated record ${record.id} in ${table.name}.`, { tableId: table.id, record });
16269
+ }
16270
+ );
16271
+ withDatabaseOptions(
16272
+ records.command("delete").description("Delete a record").argument("<table>", TABLE_ARGUMENT).argument("<recordId>", "the record id")
16273
+ ).action(async (reference, recordId, opts, command) => {
16274
+ const options = globalOptions(command);
16275
+ const context = await schemaContext(opts);
16276
+ const table = await findTable(context, reference, true);
16277
+ await deleteRecord(context.client, context.databaseId, table.id, recordId);
16278
+ emit(options, `Deleted record ${recordId} from ${table.name}.`, { tableId: table.id, deleted: recordId });
16279
+ });
16280
+ withOutputOptions(
16281
+ withDatabaseOptions(
16282
+ records.command("upload").description("Add a local file to a record's attachment field").argument("<table>", TABLE_ARGUMENT).argument("<recordId>", "the record id").argument("<field>", "the attachment field, by id or exact name").argument("<file>", "the file to upload").option("--name <fileName>", "the file name to store (default: the local file name)")
16283
+ )
16284
+ ).action(
16285
+ async (reference, recordId, fieldReference, file, opts, command) => {
16286
+ const options = globalOptions(command);
16287
+ let content;
16288
+ try {
16289
+ content = await readFile9(file);
16290
+ } catch {
16291
+ throw new CliError(`Cannot read ${file}.`);
16292
+ }
16293
+ const context = await schemaContext(opts);
16294
+ const found = await fieldIdsFor(context, await findTable(context, reference), [fieldReference]);
16295
+ const fieldId = found.ids.get(fieldReference);
16296
+ const fieldType = found.table.fields.find((field) => field.id === fieldId)?.type;
16297
+ if (fieldType !== "ATTACHMENT") {
16298
+ throw new CliError(`Field "${fieldReference}" has the type ${fieldType}; upload needs an ATTACHMENT field.`);
16299
+ }
16300
+ const uploaded = await uploadAttachment(context.client, context.databaseId, found.table.id, recordId, fieldId, {
16301
+ name: opts.name ?? path12.basename(file),
16302
+ content
16303
+ });
16304
+ const record = opts.fieldIds ? uploaded : withFieldNames(uploaded, found.table);
16305
+ emit(options, `Uploaded ${opts.name ?? path12.basename(file)} to record ${record.id} in ${found.table.name}.`, {
16306
+ tableId: found.table.id,
16307
+ record
16308
+ });
16309
+ }
16310
+ );
16311
+ }
16312
+
16313
+ // src/commands/integrations.ts
16314
+ var import_cli_table33 = __toESM(require_cli_table3(), 1);
16315
+ import path13 from "node:path";
15544
16316
  function toRow(integration) {
15545
16317
  return {
15546
16318
  id: integration.id,
@@ -15558,7 +16330,7 @@ function renderIntegrations(rows) {
15558
16330
  if (!rows.length) {
15559
16331
  return "No integrations connected in this workspace. Connect one in the studio (Workspace settings > Integrations).";
15560
16332
  }
15561
- const table = new import_cli_table32.default({
16333
+ const table = new import_cli_table33.default({
15562
16334
  head: ["ID", "Name", "Type", "Account", "Auth"],
15563
16335
  colWidths: [38, 34, 22, 30, 11],
15564
16336
  wordWrap: true,
@@ -15572,7 +16344,7 @@ Use the ID as the integrationId input of a node (see "spec <TYPE>").`;
15572
16344
  }
15573
16345
  async function workflowDirOf(cwd, dir) {
15574
16346
  if (dir) {
15575
- return path12.resolve(cwd, dir);
16347
+ return path13.resolve(cwd, dir);
15576
16348
  }
15577
16349
  return await configExists(cwd) ? cwd : void 0;
15578
16350
  }
@@ -15610,7 +16382,7 @@ ${renderIntegrations(rows)}`, {
15610
16382
  }
15611
16383
 
15612
16384
  // src/commands/list.ts
15613
- var import_cli_table33 = __toESM(require_cli_table3(), 1);
16385
+ var import_cli_table34 = __toESM(require_cli_table3(), 1);
15614
16386
  function toRow2(workflow, workspace) {
15615
16387
  return {
15616
16388
  id: workflow.id,
@@ -15652,7 +16424,7 @@ async function collectWorkflows(client, { workspaceId, applicationId } = {}) {
15652
16424
  }
15653
16425
  var TITLE_WIDTH = 34;
15654
16426
  function workflowTable(rows) {
15655
- const table = new import_cli_table33.default({
16427
+ const table = new import_cli_table34.default({
15656
16428
  head: ["ID", "Title", "Triggers", "Status", "Version", "Draft"],
15657
16429
  // Only the free-text title wraps; the other columns size to their content, so trigger types are never cut off.
15658
16430
  colWidths: [null, TITLE_WIDTH, null, null, null, null],
@@ -15973,7 +16745,7 @@ ${identity.statements.map((s) => ` ${s.actions.join(", ")} on ${s.resources.joi
15973
16745
  }
15974
16746
 
15975
16747
  // src/commands/outputs.ts
15976
- var import_cli_table35 = __toESM(require_cli_table3(), 1);
16748
+ var import_cli_table36 = __toESM(require_cli_table3(), 1);
15977
16749
 
15978
16750
  // src/project/test-state.ts
15979
16751
  function outdatedReferences(row) {
@@ -15986,28 +16758,28 @@ function renderIssues(issues) {
15986
16758
 
15987
16759
  // src/project/variables.ts
15988
16760
  var MAX_VARIABLES = 200;
15989
- function appendKey(path15, key) {
15990
- return /^[A-Za-z_]\w*$/.test(key) ? `${path15}.${key}` : `${path15}['${key.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}']`;
16761
+ function appendKey(path16, key) {
16762
+ return /^[A-Za-z_]\w*$/.test(key) ? `${path16}.${key}` : `${path16}['${key.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}']`;
15991
16763
  }
15992
16764
  function isPrimitive2(value) {
15993
16765
  return value !== void 0 && value !== null && typeof value !== "object";
15994
16766
  }
15995
- function walk(type, raw, path15, token, out) {
16767
+ function walk(type, raw, path16, token, out) {
15996
16768
  if (out.length >= MAX_VARIABLES) {
15997
16769
  return;
15998
16770
  }
15999
- out.push({ variable: token(path15), path: path15, type, ...isPrimitive2(raw) ? { example: raw } : {} });
16771
+ out.push({ variable: token(path16), path: path16, type, ...isPrimitive2(raw) ? { example: raw } : {} });
16000
16772
  if (type.kind === "object") {
16001
16773
  for (const [key, field] of Object.entries(type.fields)) {
16002
16774
  const rawField = raw !== null && typeof raw === "object" && !Array.isArray(raw) ? raw[key] : void 0;
16003
- walk(field.type, rawField, appendKey(path15, key), token, out);
16775
+ walk(field.type, rawField, appendKey(path16, key), token, out);
16004
16776
  }
16005
16777
  } else if (type.kind === "array") {
16006
- walk(type.items, Array.isArray(raw) ? raw[0] : void 0, `${path15}[*]`, token, out);
16778
+ walk(type.items, Array.isArray(raw) ? raw[0] : void 0, `${path16}[*]`, token, out);
16007
16779
  }
16008
16780
  }
16009
16781
  function collectVariables(nodeId, sample, metadata) {
16010
- const token = (path15) => `{outputs.${nodeId}:::${path15}}`;
16782
+ const token = (path16) => `{outputs.${nodeId}:::${path16}}`;
16011
16783
  const variables = [];
16012
16784
  if (sample !== void 0 && sample !== null) {
16013
16785
  walk(typeOfJson(sample), sample, "$", token, variables);
@@ -16022,7 +16794,7 @@ function collectVariables(nodeId, sample, metadata) {
16022
16794
  }
16023
16795
 
16024
16796
  // src/commands/test.ts
16025
- var import_cli_table34 = __toESM(require_cli_table3(), 1);
16797
+ var import_cli_table35 = __toESM(require_cli_table3(), 1);
16026
16798
  var OUTPUTS_FILE = "outputs.json";
16027
16799
  function fieldCount(row) {
16028
16800
  const output = row.sampleOutput?.result?.output;
@@ -16123,7 +16895,7 @@ var LABEL = {
16123
16895
  error: "ERROR"
16124
16896
  };
16125
16897
  function renderResults(results) {
16126
- const table = new import_cli_table34.default({
16898
+ const table = new import_cli_table35.default({
16127
16899
  head: ["Result", "Node", "Type", "Detail"],
16128
16900
  colWidths: [9, 30, 24, 60],
16129
16901
  wordWrap: true,
@@ -16239,7 +17011,7 @@ function sampleState(row) {
16239
17011
  }
16240
17012
  }
16241
17013
  function renderVariables(nodeId, entries, unverified) {
16242
- const table = new import_cli_table35.default({
17014
+ const table = new import_cli_table36.default({
16243
17015
  head: ["Variable", "Type", "Example"],
16244
17016
  colWidths: [64, 32, 44],
16245
17017
  wordWrap: true,
@@ -16307,7 +17079,7 @@ function registerOutputs(program3) {
16307
17079
  outdated: Object.keys(outdatedReferences(row))
16308
17080
  };
16309
17081
  });
16310
- const table = new import_cli_table35.default({
17082
+ const table = new import_cli_table36.default({
16311
17083
  head: ["Node", "Type", "Sample", "Fields"],
16312
17084
  colWidths: [30, 24, 60, 8],
16313
17085
  wordWrap: true,
@@ -16339,19 +17111,19 @@ function pathFromKeys(keys) {
16339
17111
  const only = keys[0].trim();
16340
17112
  return only.startsWith("$") ? only : `$${only.startsWith("[") ? "" : "."}${only}`;
16341
17113
  }
16342
- let path15 = "$";
17114
+ let path16 = "$";
16343
17115
  for (const key of keys) {
16344
17116
  if (key === "*") {
16345
- path15 += "[*]";
17117
+ path16 += "[*]";
16346
17118
  } else if (/^\d+$/.test(key)) {
16347
- path15 += `[${key}]`;
17119
+ path16 += `[${key}]`;
16348
17120
  } else if (/^[A-Za-z_]\w*$/.test(key)) {
16349
- path15 += `.${key}`;
17121
+ path16 += `.${key}`;
16350
17122
  } else {
16351
- path15 += `['${key.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}']`;
17123
+ path16 += `['${key.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}']`;
16352
17124
  }
16353
17125
  }
16354
- return path15;
17126
+ return path16;
16355
17127
  }
16356
17128
  function keysAt(type) {
16357
17129
  switch (type.kind) {
@@ -16376,12 +17148,12 @@ function registerPlaceholder(program3) {
16376
17148
  }
16377
17149
  const rows = await readStateFile(dir, OUTPUTS_FILE) ?? [];
16378
17150
  const outputs = indexOutputs(rows);
16379
- const path15 = pathFromKeys(keys);
16380
- if (!parsePath(path15)) {
16381
- throw new CliError(`Cannot read the path "${path15}".`);
17151
+ const path16 = pathFromKeys(keys);
17152
+ if (!parsePath(path16)) {
17153
+ throw new CliError(`Cannot read the path "${path16}".`);
16382
17154
  }
16383
17155
  const isLoop = location.node.type === LOOP_TYPE;
16384
- const token = isLoop ? `{loopActionGroup.${nodeId}:::loopVariables.items${path15 === "$" ? "" : path15.slice(1)}}` : `{outputs.${nodeId}:::${path15}}`;
17156
+ const token = isLoop ? `{loopActionGroup.${nodeId}:::loopVariables.items${path16 === "$" ? "" : path16.slice(1)}}` : `{outputs.${nodeId}:::${path16}}`;
16385
17157
  const [description] = describeInputData(
16386
17158
  { id: "probe", type: "CUSTOM_CODE", inputs: { inputData: { probe: token } } },
16387
17159
  config,
@@ -16391,7 +17163,7 @@ function registerPlaceholder(program3) {
16391
17163
  throw new CliError("Could not evaluate the token.");
16392
17164
  }
16393
17165
  if (description.type.kind === "unknown") {
16394
- const segments = parsePath(path15) ?? [];
17166
+ const segments = parsePath(path16) ?? [];
16395
17167
  let hint = description.note ?? "not found";
16396
17168
  const row = outputs[nodeId];
16397
17169
  if (row?.hasSample && segments.length) {
@@ -16417,7 +17189,7 @@ function registerPlaceholder(program3) {
16417
17189
  emit(options, `${token}
16418
17190
  type: ${type}${example3} (${status})`, {
16419
17191
  token,
16420
- path: path15,
17192
+ path: path16,
16421
17193
  type,
16422
17194
  example: description.example ?? null,
16423
17195
  verified: description.verified,
@@ -16427,10 +17199,10 @@ type: ${type}${example3} (${status})`, {
16427
17199
  }
16428
17200
 
16429
17201
  // src/commands/pull.ts
16430
- import path13 from "node:path";
17202
+ import path14 from "node:path";
16431
17203
  async function targetDir(cwd, explicitDir, workflowId, title) {
16432
17204
  if (explicitDir) {
16433
- return path13.resolve(cwd, explicitDir);
17205
+ return path14.resolve(cwd, explicitDir);
16434
17206
  }
16435
17207
  if (await configExists(cwd)) {
16436
17208
  const existing = await readConfig(cwd);
@@ -16442,13 +17214,13 @@ async function targetDir(cwd, explicitDir, workflowId, title) {
16442
17214
  }
16443
17215
  return cwd;
16444
17216
  }
16445
- return path13.join(cwd, slug(title));
17217
+ return path14.join(cwd, slug(title));
16446
17218
  }
16447
17219
  function registerPull(program3) {
16448
17220
  program3.command("pull").description("Download a workflow into softr-workflows.jsonc and one code file per custom code node").argument("[workflowId]", "the workflow id (last segment of its studio URL); omit to list what you can pull").option("--dir <path>", "target directory (default: this directory if it holds the workflow, else ./<title-slug>)").option("--force", "overwrite an existing softr-workflows.jsonc and its code files").action(async (workflowId, opts, command) => {
16449
17221
  const options = globalOptions(command);
16450
17222
  const cwd = process.cwd();
16451
- const remembered = await readState(opts.dir ? path13.resolve(cwd, opts.dir) : cwd);
17223
+ const remembered = await readState(opts.dir ? path14.resolve(cwd, opts.dir) : cwd);
16452
17224
  const target = resolveTarget(remembered);
16453
17225
  const client = await authenticatedClient(target);
16454
17226
  if (!workflowId) {
@@ -16461,7 +17233,7 @@ function registerPull(program3) {
16461
17233
  const dir = await targetDir(cwd, opts.dir, workflowId, workflow.title);
16462
17234
  if (await configExists(dir) && !opts.force) {
16463
17235
  throw new CliError(
16464
- `${path13.join(dir, CONFIG_FILE_NAME)} already exists.`,
17236
+ `${path14.join(dir, CONFIG_FILE_NAME)} already exists.`,
16465
17237
  "Pass --force to overwrite it and its code files."
16466
17238
  );
16467
17239
  }
@@ -16483,7 +17255,7 @@ Sample outputs not available, inputData types are unknown: ${error.message}`;
16483
17255
  if (!outputsNote) {
16484
17256
  outputsFile = await writeStateFile(dir, "outputs.json", rows);
16485
17257
  }
16486
- const files = [configFile2, ...codeFiles].map((file) => path13.relative(cwd, file));
17258
+ const files = [configFile2, ...codeFiles].map((file) => path14.relative(cwd, file));
16487
17259
  emitWithVersionNote(
16488
17260
  options,
16489
17261
  config,
@@ -16741,23 +17513,23 @@ function registerPin(program3) {
16741
17513
  }
16742
17514
 
16743
17515
  // src/commands/push.ts
16744
- import path14 from "node:path";
17516
+ import path15 from "node:path";
16745
17517
 
16746
17518
  // src/project/placeholder-check.ts
16747
17519
  var TOKEN = /\{(?:outputs|loopActionGroup)\.[^:{}]+:::[^{}]*\}/g;
16748
- function* stringsIn(value, path15) {
17520
+ function* stringsIn(value, path16) {
16749
17521
  if (typeof value === "string") {
16750
- yield [path15, value];
17522
+ yield [path16, value];
16751
17523
  } else if (Array.isArray(value)) {
16752
17524
  for (const [index, item] of value.entries()) {
16753
- yield* stringsIn(item, `${path15}[${index}]`);
17525
+ yield* stringsIn(item, `${path16}[${index}]`);
16754
17526
  }
16755
17527
  } else if (value !== null && typeof value === "object") {
16756
17528
  for (const [key, item] of Object.entries(value)) {
16757
17529
  if (key === "actions" || key === "paths") {
16758
17530
  continue;
16759
17531
  }
16760
- yield* stringsIn(item, path15 ? `${path15}.${key}` : key);
17532
+ yield* stringsIn(item, path16 ? `${path16}.${key}` : key);
16761
17533
  }
16762
17534
  }
16763
17535
  }
@@ -16868,7 +17640,7 @@ ${unbacked.map(
16868
17640
  draft: result.draft,
16869
17641
  inlined,
16870
17642
  studioUrl,
16871
- dir: path14.resolve(dir),
17643
+ dir: path15.resolve(dir),
16872
17644
  unbackedPlaceholders: unbacked
16873
17645
  }
16874
17646
  );
@@ -16876,10 +17648,10 @@ ${unbacked.map(
16876
17648
  }
16877
17649
 
16878
17650
  // src/commands/spec.ts
16879
- var import_cli_table36 = __toESM(require_cli_table3(), 1);
17651
+ var import_cli_table37 = __toESM(require_cli_table3(), 1);
16880
17652
  var TABLE_STYLE2 = { head: [], border: [], "padding-left": 1, "padding-right": 1 };
16881
17653
  function renderList() {
16882
- const table = new import_cli_table36.default({
17654
+ const table = new import_cli_table37.default({
16883
17655
  head: ["Type", "Kind", "Label", "Summary"],
16884
17656
  colWidths: [40, 9, 24, 70],
16885
17657
  wordWrap: true,
@@ -16907,7 +17679,7 @@ function renderEntry(entry) {
16907
17679
  const blocks = [`${entry.type} (${entry.kind}) \u2014 ${entry.label}`, entry.summary, `Docs: ${entry.docs}`];
16908
17680
  const inputs = Object.entries(entry.inputs);
16909
17681
  if (inputs.length) {
16910
- const table = new import_cli_table36.default({
17682
+ const table = new import_cli_table37.default({
16911
17683
  head: ["Input", "Type", "Required", "Description"],
16912
17684
  colWidths: [20, 9, 10, 90],
16913
17685
  wordWrap: true,
@@ -17157,14 +17929,14 @@ ${item.lines.map((line) => ` ${line}`).join("\n")}`
17157
17929
  }
17158
17930
 
17159
17931
  // src/commands/workspaces.ts
17160
- var import_cli_table37 = __toESM(require_cli_table3(), 1);
17932
+ var import_cli_table38 = __toESM(require_cli_table3(), 1);
17161
17933
  function registerWorkspaces(program3) {
17162
17934
  program3.command("workspaces").description("The workspaces the stored token reaches, with your role and the scopes the token grants in each").action(async (_opts, command) => {
17163
17935
  const options = globalOptions(command);
17164
17936
  const target = resolveTarget();
17165
17937
  const client = await authenticatedClient(target);
17166
17938
  const workspaces = await listWorkspaces(client);
17167
- const table = new import_cli_table37.default({
17939
+ const table = new import_cli_table38.default({
17168
17940
  head: ["ID", "Name", "Role", "Default", "Scopes"],
17169
17941
  colWidths: [38, 26, 10, 9, 34],
17170
17942
  wordWrap: true,
@@ -17191,7 +17963,7 @@ Pass --workspace <id> to a command, or set SOFTR_WORKSPACE_ID, to work in one of
17191
17963
  // package.json
17192
17964
  var package_default = {
17193
17965
  name: "@softr/cli",
17194
- version: "0.1.4",
17966
+ version: "0.1.6-se21219.17",
17195
17967
  description: "The Softr command line: build, test and publish Softr workflows from local files, manage Softr Databases; more Softr APIs to come",
17196
17968
  keywords: [
17197
17969
  "softr",
@@ -17219,7 +17991,7 @@ var package_default = {
17219
17991
  format: "prettier --write .",
17220
17992
  "format:check": "prettier --check .",
17221
17993
  check: "npm run typecheck && npm run format:check && npm test && npm run build",
17222
- "check:skill": "npx -y skills-ref@0.1.5 validate skills/softr-cli && npx -y skills-ref@0.1.5 validate skills/softr-workflows"
17994
+ "check:skill": "npx -y skills-ref@0.1.5 validate skills/softr-cli && npx -y skills-ref@0.1.5 validate skills/softr-workflows && npx -y skills-ref@0.1.5 validate skills/softr-db"
17223
17995
  },
17224
17996
  publishConfig: {
17225
17997
  access: "public"
@@ -17264,4 +18036,8 @@ registerSpec(workflows);
17264
18036
  registerSchema(workflows);
17265
18037
  var db = program2.command("db").description("Manage Softr Databases: databases, their tables, fields and records");
17266
18038
  registerDatabases(db);
18039
+ registerTables(db);
18040
+ registerFields(db);
18041
+ registerViews(db);
18042
+ registerRecords(db);
17267
18043
  program2.parseAsync(process.argv).catch(reportError);