@datarails-ai/dr-appspace-cli 0.10.6 → 0.10.7

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/cli.js +103 -10
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ import { fileURLToPath as fileURLToPath6 } from "url";
13
13
  // package.json
14
14
  var package_default = {
15
15
  name: "@datarails-ai/dr-appspace-cli",
16
- version: "0.10.6",
16
+ version: "0.10.7",
17
17
  description: "Datarails App Space CLI \u2014 build, try, push and publish App Space apps",
18
18
  type: "module",
19
19
  engines: {
@@ -57,7 +57,7 @@ var package_default = {
57
57
  };
58
58
 
59
59
  // src/buildInfo.ts
60
- var BUILD_TIME = "2026-09-30T16:43:46.444Z";
60
+ var BUILD_TIME = "2026-09-30T19:32:36.822Z";
61
61
  function formatBuildTime(iso) {
62
62
  if (iso === "") return "";
63
63
  const d = new Date(iso);
@@ -2793,10 +2793,53 @@ function conflictTables(body) {
2793
2793
  const entries = Object.entries(raw).filter((e) => Array.isArray(e[1]) && e[1].length > 0).map(([table, owners]) => [table, owners.map(String)]);
2794
2794
  return entries.length === 0 ? void 0 : entries;
2795
2795
  }
2796
+ function conflictOwners(body, table) {
2797
+ const owners = (body?.conflicts?.[table] ?? {}).owners;
2798
+ if (!Array.isArray(owners)) return [];
2799
+ return owners.flatMap((o) => {
2800
+ if (typeof o !== "object" || o === null) return [];
2801
+ const r = o;
2802
+ if (typeof r["app_id"] !== "string") return [];
2803
+ const cols = r["columns"];
2804
+ return [
2805
+ {
2806
+ appId: r["app_id"],
2807
+ columns: typeof cols === "object" && cols !== null && !Array.isArray(cols) ? Object.fromEntries(Object.entries(cols).map(([k, v]) => [k, String(v)])) : null,
2808
+ connectProblems: Array.isArray(r["connect_problems"]) ? r["connect_problems"].map(String) : []
2809
+ }
2810
+ ];
2811
+ });
2812
+ }
2813
+ function columnsLine(columns) {
2814
+ return Object.entries(columns).map(([n, t]) => `${n} ${t}`).join(", ");
2815
+ }
2816
+ function conflictGuidance(body) {
2817
+ const lines = [];
2818
+ for (const [table, ids] of conflictTables(body) ?? []) {
2819
+ const owners = conflictOwners(body, table);
2820
+ for (const owner of owners.length > 0 ? owners : ids.map((appId) => ({ appId, columns: null, connectProblems: [] }))) {
2821
+ lines.push(
2822
+ `table "${table}" is owned by ${owner.appId}` + (owner.columns === null ? " (its structure could not be read)" : ` with columns: ${columnsLine(owner.columns)}`)
2823
+ );
2824
+ if (owner.connectProblems.length > 0) {
2825
+ lines.push(
2826
+ ` as declared, connecting would be refused: ${owner.connectProblems.join("; ")}`,
2827
+ " Match the owner's columns and types exactly (tables share ONE structure: the owner's), or rename yours."
2828
+ );
2829
+ } else if (owners.length > 0) {
2830
+ lines.push(
2831
+ ` to share it, add to manifest.json: "connect_tables": ${JSON.stringify({ [table]: owner.appId })}`
2832
+ );
2833
+ }
2834
+ }
2835
+ }
2836
+ return lines;
2837
+ }
2796
2838
  function describeFailure(status, method, path3, body) {
2797
2839
  const conflicts = conflictTables(body);
2798
2840
  if (status === 409 && conflicts !== void 0) {
2799
- return `${method} ${path3} failed (409) \u2014 ${conflicts.map(([table, owners]) => `table "${table}" already belongs to ${owners.join(", ")}`).join("; ")}. Rename the table in data_model.json, or push with \`connect_tables\` to share the existing one.`;
2841
+ return `${method} ${path3} failed (409) \u2014 ${conflicts.map(([table, owners]) => `table "${table}" already belongs to ${owners.join(", ")}`).join("; ")}. Rename the table in data_model.json, or share the existing one with \`connect_tables\` in manifest.json.` + conflictGuidance(body).map((l) => `
2842
+ ${l}`).join("");
2800
2843
  }
2801
2844
  const code = body?.detail?.error;
2802
2845
  if (status === 403 && code === "studio_not_enabled") {
@@ -6941,6 +6984,10 @@ async function pushBody(dir, opts) {
6941
6984
  if (ui?.field === "frontend_bundle_gz") {
6942
6985
  body["frontend_bundle_gz"] = ui.files;
6943
6986
  }
6987
+ const connectTables = manifest["connect_tables"];
6988
+ if (typeof connectTables === "object" && connectTables !== null && !Array.isArray(connectTables)) {
6989
+ if (Object.keys(connectTables).length > 0) body["connect_tables"] = connectTables;
6990
+ }
6944
6991
  if (contract !== null) {
6945
6992
  body["contract"] = contract;
6946
6993
  }
@@ -7010,6 +7057,10 @@ async function pushFolder(dir, opts) {
7010
7057
  }
7011
7058
  if (result.stage !== "published") {
7012
7059
  process.stdout.write("Only you can see it until an admin publishes it.\n");
7060
+ } else if (opts.publish !== true) {
7061
+ process.stdout.write(
7062
+ "The app was already published: this push changed its working version only; the published one is unchanged until it is published again.\n"
7063
+ );
7013
7064
  }
7014
7065
  return outcome;
7015
7066
  } finally {
@@ -7146,9 +7197,11 @@ async function writePackage(dir, appId, pkg) {
7146
7197
  rm2(join10(dir, "handlers"), { recursive: true, force: true }),
7147
7198
  rm2(join10(dir, "ui"), { recursive: true, force: true })
7148
7199
  ]);
7200
+ const connected = pkg.connect_tables ?? {};
7201
+ const manifestOut = Object.keys(connected).length > 0 ? { ...pkg.manifest, connect_tables: connected } : pkg.manifest;
7149
7202
  await writeFile5(
7150
7203
  join10(dir, "manifest.json"),
7151
- JSON.stringify(pkg.manifest, null, 2) + "\n"
7204
+ JSON.stringify(manifestOut, null, 2) + "\n"
7152
7205
  );
7153
7206
  if (pkg.data_model !== null && pkg.data_model !== void 0) {
7154
7207
  await writeFile5(
@@ -10763,12 +10816,19 @@ function fromRefusal(status, detail, contract) {
10763
10816
  const tables = tablesOf(contract);
10764
10817
  const conflicts = conflictTables(detail);
10765
10818
  if (conflicts !== void 0) {
10766
- return conflicts.map(([table, owners]) => ({
10767
- code: "server:table-conflict",
10768
- pointer: tables.has(table) ? pointer("app", "data_model", "tables", table) : "/",
10769
- message: `${refused}: table "${table}" already belongs to ${owners.join(", ")}.`,
10770
- fix: "Rename the table, or push with connect_tables to share the existing one."
10771
- }));
10819
+ return conflicts.map(([table, owners]) => {
10820
+ const owned = conflictOwners(detail, table);
10821
+ const detailLines = conflictGuidance({
10822
+ conflict_tables: { [table]: owners },
10823
+ conflicts: detail.conflicts
10824
+ });
10825
+ return {
10826
+ code: "server:table-conflict",
10827
+ pointer: tables.has(table) ? pointer("app", "data_model", "tables", table) : "/",
10828
+ message: `${refused}: table "${table}" already belongs to ${owners.join(", ")}.` + (owned.length > 0 ? ` ${detailLines.join(" ")}` : ""),
10829
+ fix: `Rename the table, or share the existing one: add "connect_tables" to manifest.json (the entry is in the message) with your columns matching the owner's exactly.`
10830
+ };
10831
+ });
10772
10832
  }
10773
10833
  if (isObj3(detail) && detail["error"] === "stale_revision") {
10774
10834
  return [
@@ -29989,6 +30049,38 @@ ${METRIC_ADVICE}
29989
30049
  );
29990
30050
  }
29991
30051
 
30052
+ // src/commands/appspace/tables.ts
30053
+ function renderTables(rows2) {
30054
+ if (rows2.length === 0) return "No app owns a table you can see.\n";
30055
+ return rows2.map(
30056
+ (r) => `${r.table} (owner ${r.owner_app}${r.stage ? `, ${r.stage}` : ""})
30057
+ columns: ${columnsLine(r.columns)}
30058
+ to share: "connect_tables": ${JSON.stringify(r.connect_tables)}
30059
+ `
30060
+ ).join("");
30061
+ }
30062
+ function registerTablesCommand(parent) {
30063
+ withJsonOption(
30064
+ withServerOptions(
30065
+ parent.command("tables").description(
30066
+ "List the tables this tenant's apps own, with their columns and owner app (to share one, connect to it)."
30067
+ )
30068
+ )
30069
+ ).action(async (opts) => {
30070
+ const client = await connect(opts);
30071
+ try {
30072
+ const rows2 = await client.request("/apps/tables");
30073
+ if (opts.json === true) {
30074
+ printJson(rows2);
30075
+ return;
30076
+ }
30077
+ process.stdout.write(renderTables(rows2));
30078
+ } finally {
30079
+ client.cleanup();
30080
+ }
30081
+ });
30082
+ }
30083
+
29992
30084
  // src/commands/appspace/whoami.ts
29993
30085
  function registerWhoamiCommand2(parent) {
29994
30086
  withJsonOption(
@@ -30027,6 +30119,7 @@ function registerAppSpaceCommand(root) {
30027
30119
  const cmd = root.command("app-space").description("Build and deploy App Space apps.");
30028
30120
  registerWhoamiCommand2(cmd);
30029
30121
  registerListCommand(cmd);
30122
+ registerTablesCommand(cmd);
30030
30123
  registerScaffoldCommand(cmd);
30031
30124
  registerGetCommand(cmd);
30032
30125
  registerLogsCommand(cmd);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datarails-ai/dr-appspace-cli",
3
- "version": "0.10.6",
3
+ "version": "0.10.7",
4
4
  "description": "Datarails App Space CLI — build, try, push and publish App Space apps",
5
5
  "type": "module",
6
6
  "engines": {