Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 14 additions & 17 deletions COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,9 +426,9 @@ altertable app create|dev|build|lint|upgrade|validate|publish|update
- `build` — Typecheck and build a local data app without Altertable credentials.
- `lint` — Lint a local data app project: format, lint rules, types, contracts, build, and client credential boundary.
- `upgrade` — Update a local data app's package to the CLI's tested version.
- `validate` — Type-check and bundle a data app from files without saving.
- `publish` — Create a data app from files.
- `update` — Update a data app from files. Omitted fields are left unchanged.
- `validate` — Type-check and bundle a data app from source without saving.
- `publish` — Create a data app from source.
- `update` — Update a data app from source. Omitted fields are left unchanged.

**Examples**

Expand All @@ -438,8 +438,8 @@ altertable app dev
altertable app lint
altertable app build
altertable app upgrade
altertable app validate --file index.tsx --queries queries.json
altertable app publish --title 'Revenue explorer' --file index.tsx --queries queries.json
altertable app validate --file index.tsx
altertable app publish --title 'Revenue explorer' --file index.tsx
altertable app update APP-1 --file index.tsx
```

Expand Down Expand Up @@ -571,7 +571,7 @@ altertable app upgrade --dir ./my-app

#### `altertable app validate`

Type-check and bundle a data app from files without saving.
Type-check and bundle a data app from source without saving.

**Usage**

Expand All @@ -583,18 +583,17 @@ altertable app validate [options]

| Option | Description |
| --- | --- |
| `--file <FILE>` | Path to the single-file React source (index.tsx). Required. |
| `--queries <QUERIES>` | Path to queries JSON (object of id → SQL, or [{id, sql}]). Required. |
| `--file <FILE>` | Path to the single-file React source (index.tsx). Queries are declared in the file via defineDataApp. Required. |

**Examples**

```bash
altertable app validate --file index.tsx --queries queries.json
altertable app validate --file index.tsx
```

#### `altertable app publish`

Create a data app from files.
Create a data app from source.

**Usage**

Expand All @@ -607,19 +606,18 @@ altertable app publish [options]
| Option | Description |
| --- | --- |
| `--title <TITLE>` | Title of the data app. Required. |
| `--file <FILE>` | Path to the single-file React source (index.tsx). Required. |
| `--queries <QUERIES>` | Path to queries JSON (object of id → SQL, or [{id, sql}]). Required. |
| `--file <FILE>` | Path to the single-file React source (index.tsx). Queries are declared in the file via defineDataApp. Required. |
| `--description <DESCRIPTION>` | Optional description of the data app. |

**Examples**

```bash
altertable app publish --title 'Revenue explorer' --file index.tsx --queries queries.json
altertable app publish --title 'Revenue explorer' --file index.tsx
```

#### `altertable app update`

Update a data app from files. Omitted fields are left unchanged.
Update a data app from source. Omitted fields are left unchanged.

**Usage**

Expand All @@ -638,15 +636,14 @@ altertable app update [options] <SLUG>
| Option | Description |
| --- | --- |
| `--title <TITLE>` | New title of the data app. |
| `--file <FILE>` | Path to replacement single-file React source (index.tsx). |
| `--queries <QUERIES>` | Path to queries JSON. Replaces the full set when provided. |
| `--file <FILE>` | Path to replacement single-file React source (index.tsx). Replaces the queries with those the file declares. |
| `--description <DESCRIPTION>` | New description of the data app. |

**Examples**

```bash
altertable app update APP-1 --file index.tsx
altertable app update APP-1 --title 'Revenue explorer' --file index.tsx --queries queries.json
altertable app update APP-1 --title 'Revenue explorer' --file index.tsx
```

### `altertable update`
Expand Down
52 changes: 11 additions & 41 deletions cli-reference.json
Original file line number Diff line number Diff line change
Expand Up @@ -816,8 +816,8 @@
"altertable app lint",
"altertable app build",
"altertable app upgrade",
"altertable app validate --file index.tsx --queries queries.json",
"altertable app publish --title 'Revenue explorer' --file index.tsx --queries queries.json",
"altertable app validate --file index.tsx",
"altertable app publish --title 'Revenue explorer' --file index.tsx",
"altertable app update APP-1 --file index.tsx"
],
"subcommands": [
Expand Down Expand Up @@ -1003,7 +1003,7 @@
{
"id": "altertable-app-validate",
"command": "altertable app validate",
"description": "Type-check and bundle a data app from files without saving.",
"description": "Type-check and bundle a data app from source without saving.",
"usage": [
"altertable app validate [options]"
],
Expand All @@ -1014,32 +1014,22 @@
"name": "file",
"aliases": [],
"type": "string",
"description": "Path to the single-file React source (index.tsx).",
"required": true,
"repeatable": false,
"scope": "command",
"values": []
},
{
"name": "queries",
"aliases": [],
"type": "string",
"description": "Path to queries JSON (object of id → SQL, or [{id, sql}]).",
"description": "Path to the single-file React source (index.tsx). Queries are declared in the file via defineDataApp.",
"required": true,
"repeatable": false,
"scope": "command",
"values": []
}
],
"examples": [
"altertable app validate --file index.tsx --queries queries.json"
"altertable app validate --file index.tsx"
],
"subcommands": []
},
{
"id": "altertable-app-publish",
"command": "altertable app publish",
"description": "Create a data app from files.",
"description": "Create a data app from source.",
"usage": [
"altertable app publish [options]"
],
Expand All @@ -1060,17 +1050,7 @@
"name": "file",
"aliases": [],
"type": "string",
"description": "Path to the single-file React source (index.tsx).",
"required": true,
"repeatable": false,
"scope": "command",
"values": []
},
{
"name": "queries",
"aliases": [],
"type": "string",
"description": "Path to queries JSON (object of id → SQL, or [{id, sql}]).",
"description": "Path to the single-file React source (index.tsx). Queries are declared in the file via defineDataApp.",
"required": true,
"repeatable": false,
"scope": "command",
Expand All @@ -1088,14 +1068,14 @@
}
],
"examples": [
"altertable app publish --title 'Revenue explorer' --file index.tsx --queries queries.json"
"altertable app publish --title 'Revenue explorer' --file index.tsx"
],
"subcommands": []
},
{
"id": "altertable-app-update",
"command": "altertable app update",
"description": "Update a data app from files. Omitted fields are left unchanged.",
"description": "Update a data app from source. Omitted fields are left unchanged.",
"usage": [
"altertable app update [options] <SLUG>"
],
Expand Down Expand Up @@ -1128,17 +1108,7 @@
"name": "file",
"aliases": [],
"type": "string",
"description": "Path to replacement single-file React source (index.tsx).",
"required": false,
"repeatable": false,
"scope": "command",
"values": []
},
{
"name": "queries",
"aliases": [],
"type": "string",
"description": "Path to queries JSON. Replaces the full set when provided.",
"description": "Path to replacement single-file React source (index.tsx). Replaces the queries with those the file declares.",
"required": false,
"repeatable": false,
"scope": "command",
Expand All @@ -1157,7 +1127,7 @@
],
"examples": [
"altertable app update APP-1 --file index.tsx",
"altertable app update APP-1 --title 'Revenue explorer' --file index.tsx --queries queries.json"
"altertable app update APP-1 --title 'Revenue explorer' --file index.tsx"
],
"subcommands": []
}
Expand Down
4 changes: 2 additions & 2 deletions cli/src/commands/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export const appCommand = defineCommand({
"altertable app lint",
"altertable app build",
"altertable app upgrade",
"altertable app validate --file index.tsx --queries queries.json",
"altertable app publish --title 'Revenue explorer' --file index.tsx --queries queries.json",
"altertable app validate --file index.tsx",
"altertable app publish --title 'Revenue explorer' --file index.tsx",
"altertable app update APP-1 --file index.tsx",
],
},
Expand Down
40 changes: 0 additions & 40 deletions cli/src/commands/app/lib/remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ import { readFile } from "node:fs/promises";
import { CliError, EXIT_VALIDATION } from "@/lib/errors.ts";
import type { HttpRequest } from "@/lib/http-request.ts";

export type DataAppQuery = { id: string; sql: string };

export type RemoteDataAppPayload = {
title?: string;
description?: string;
index_tsx?: string;
queries?: DataAppQuery[];
};

export type ValidationDiagnostic = {
Expand Down Expand Up @@ -37,43 +34,6 @@ export async function readRequiredFile(path: string, label: string): Promise<str
}
}

export async function readJsonFile(path: string, label: string): Promise<unknown> {
const text = await readRequiredFile(path, label);
try {
return JSON.parse(text) as unknown;
} catch (error) {
throw new CliError(`${label} is not valid JSON: ${path}`, { cause: error });
}
}

export function parseQueries(value: unknown): DataAppQuery[] {
if (Array.isArray(value)) {
return value.map((entry, index) => {
if (typeof entry !== "object" || entry === null) {
throw new CliError(`queries[${index}] must be an object with id and sql.`);
}
const { id, sql } = entry as { id?: unknown; sql?: unknown };
if (typeof id !== "string" || typeof sql !== "string") {
throw new CliError(`queries[${index}] id and sql must be strings.`);
}
return { id, sql };
});
}
if (typeof value === "object" && value !== null) {
return Object.entries(value as Record<string, unknown>).map(([id, sql]) => {
if (typeof sql !== "string") {
throw new CliError(`queries.${id} must be a SQL string.`);
}
return { id, sql };
});
}
throw new CliError("queries must be a JSON object or an array of {id, sql}.");
}

export async function readQueriesFile(path: string): Promise<DataAppQuery[]> {
return parseQueries(await readJsonFile(path, "queries"));
}

export function buildDataAppHttpRequest(
env: string,
method: string,
Expand Down
17 changes: 4 additions & 13 deletions cli/src/commands/app/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,22 @@ import { sendHttp } from "@/lib/http-request.ts";
import { parseApiJson } from "@/lib/parse-api-json.ts";
import {
buildDataAppHttpRequest,
readQueriesFile,
readRequiredFile,
type PublishedDataApp,
} from "@/commands/app/lib/remote.ts";

export const appPublishCommand = defineCommand({
metadata: {
name: "publish",
description: "Create a data app from files.",
examples: [
"altertable app publish --title 'Revenue explorer' --file index.tsx --queries queries.json",
],
description: "Create a data app from source.",
examples: ["altertable app publish --title 'Revenue explorer' --file index.tsx"],
},
args: {
title: { type: "string", description: "Title of the data app.", required: true },
file: {
type: "string",
description: "Path to the single-file React source (index.tsx).",
required: true,
completion: "file",
},
queries: {
type: "string",
description: "Path to queries JSON (object of id → SQL, or [{id, sql}]).",
description:
"Path to the single-file React source (index.tsx). Queries are declared in the file via defineDataApp.",
required: true,
completion: "file",
},
Expand All @@ -45,7 +37,6 @@ export const appPublishCommand = defineCommand({
buildDataAppHttpRequest(env, "POST", "/data_apps", {
title,
index_tsx: await readRequiredFile(stringArg(args, "file"), "source"),
queries: await readQueriesFile(stringArg(args, "queries")),
description: optionalStringArg(args, "description"),
}),
execution,
Expand Down
Loading
Loading