@lightdash/cli 1.45.0 → 1.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +39 -4
- package/dist/config.test.d.ts +2 -0
- package/dist/config.test.d.ts.map +1 -0
- package/dist/config.test.js +40 -0
- package/dist/handlers/apps/appCodeFiles.d.ts +1 -0
- package/dist/handlers/apps/appCodeFiles.d.ts.map +1 -1
- package/dist/handlers/apps/appCodeFiles.js +7 -4
- package/dist/handlers/apps/appCodeFiles.test.js +8 -0
- package/dist/handlers/apps/preview.d.ts +56 -0
- package/dist/handlers/apps/preview.d.ts.map +1 -0
- package/dist/handlers/apps/preview.js +262 -0
- package/dist/handlers/apps/preview.test.d.ts +2 -0
- package/dist/handlers/apps/preview.test.d.ts.map +1 -0
- package/dist/handlers/apps/preview.test.js +189 -0
- package/dist/handlers/apps/previewProxy.d.ts +20 -0
- package/dist/handlers/apps/previewProxy.d.ts.map +1 -0
- package/dist/handlers/apps/previewProxy.js +144 -0
- package/dist/handlers/apps/previewProxy.test.d.ts +2 -0
- package/dist/handlers/apps/previewProxy.test.d.ts.map +1 -0
- package/dist/handlers/apps/previewProxy.test.js +163 -0
- package/dist/handlers/apps/scaffolding.test.js +7 -0
- package/dist/handlers/utils.d.ts +8 -0
- package/dist/handlers/utils.d.ts.map +1 -1
- package/dist/handlers/utils.js +1 -0
- package/dist/index.js +16 -4
- package/dist/vendor/authoring/AGENTS.md.tmpl +3 -3
- package/dist/vendor/authoring/README.md.tmpl +7 -4
- package/dist/vendor/authoring/developing-data-apps-locally/SKILL.md +19 -6
- package/dist/vendor/authoring/gitignore +1 -0
- package/dist/vendor/template/vite.config.js +63 -3
- package/package.json +3 -3
|
@@ -4,14 +4,17 @@ Downloaded with the Lightdash CLI (`lightdash download`).
|
|
|
4
4
|
|
|
5
5
|
## Edit → build → upload
|
|
6
6
|
1. Edit files under `src/`.
|
|
7
|
-
2. Optionally, `
|
|
8
|
-
3.
|
|
7
|
+
2. Optionally, `npm install && npm run build` to check it compiles locally. If install fails in your environment, skip it — the server build on upload is authoritative and surfaces errors on the app page. Don't change machine or registry config to force it.
|
|
8
|
+
3. Optionally, preview against real data: `lightdash apps preview` (needs a successful `npm install`).
|
|
9
|
+
4. `lightdash upload --apps` — Lightdash rebuilds and serves the app.
|
|
10
|
+
|
|
11
|
+
Local preview uses your existing CLI login through a project- and route-restricted loopback proxy; the credential is not written into this folder or exposed to browser code. It runs local tooling as your OS user (with access to that user's files, including the existing CLI config) and shows data under your own permissions, so preview only source/dependencies you trust. Host-mediated features such as external connections and data-app-viz context still need testing after upload.
|
|
9
12
|
|
|
10
13
|
## What's read-only
|
|
11
14
|
Most root config is read-only: `vite.config.js`, `tailwind.config.js`, `tsconfig.json`, etc. The server rebuilds against a trusted template, so local edits to those files don't change the deployed app.
|
|
12
15
|
|
|
13
|
-
The app builds against the template's fixed dependency set (see `package.json`) — design within it. Only when custom dependencies are enabled on your Lightdash instance (most instances: not enabled), `package.json` becomes partially editable: run `pnpm add <pkg>` to declare extra npm dependencies (registry packages, plain semver versions only, up to 60 direct deps). `pnpm-lock.yaml` must be updated to match — `pnpm add` does this; if you edit `package.json` by hand, run `pnpm install --lockfile-only`. Upload rejects new dependencies without a matching lockfile. On upload the CLI warns which packages will be installed in the build sandbox; install scripts never run, and the server replaces `scripts` with the trusted template's (the sandbox build command is not uploader-controlled). The AI builder and in-app UI cannot change the dependency set — dependencies are declared by editing `package.json` and re-uploading.
|
|
16
|
+
The app builds against the template's fixed dependency set (see `package.json`) — design within it. Only when custom dependencies are enabled on your Lightdash instance (most instances: not enabled), `package.json` becomes partially editable. This is the one workflow that still requires pnpm: run `pnpm add <pkg>` to declare extra npm dependencies (registry packages, plain semver versions only, up to 60 direct deps). `pnpm-lock.yaml` must be updated to match — `pnpm add` does this; if you edit `package.json` by hand, run `pnpm install --lockfile-only`. Upload rejects new dependencies without a matching lockfile. On upload the CLI warns which packages will be installed in the build sandbox; install scripts never run, and the server replaces `scripts` with the trusted template's (the sandbox build command is not uploader-controlled). The AI builder and in-app UI cannot change the dependency set — dependencies are declared by editing `package.json` and re-uploading.
|
|
14
17
|
|
|
15
|
-
`.npmrc` sets `ignore-scripts=true` so dependencies' lifecycle scripts never run on your machine either — downloaded apps may be authored by someone else. Keep it; explicit `
|
|
18
|
+
`.npmrc` sets `ignore-scripts=true` so dependencies' lifecycle scripts never run on your machine either — downloaded apps may be authored by someone else. Keep it; explicit commands such as `npm run build` still work.
|
|
16
19
|
|
|
17
20
|
`.lightdash/context/` is a point-in-time snapshot of the source project; re-download to refresh it.
|
|
@@ -9,7 +9,7 @@ You are editing a Lightdash **data app** that was downloaded with the Lightdash
|
|
|
9
9
|
|
|
10
10
|
## The only way to reach data is the SDK
|
|
11
11
|
|
|
12
|
-
- All data access goes through `@lightdash/query-sdk
|
|
12
|
+
- All app data access goes through `@lightdash/query-sdk`. Deployed apps use Lightdash's postMessage bridge; `lightdash apps preview` uses a loopback proxy restricted to the same SDK routes and project. Do not add direct `fetch` or REST calls.
|
|
13
13
|
- For the SDK surface (query builder, `useLightdash`, filters, downloads), read the `lightdash-data-app` skill in this folder.
|
|
14
14
|
|
|
15
15
|
## External HTTP APIs go through linked connections
|
|
@@ -28,16 +28,29 @@ You are editing a Lightdash **data app** that was downloaded with the Lightdash
|
|
|
28
28
|
## The edit → build → upload loop
|
|
29
29
|
|
|
30
30
|
1. Edit files under `src/` only.
|
|
31
|
-
2. Optionally, `
|
|
31
|
+
2. Optionally, `npm install` then `npm run build` to check it compiles. This is an **optional local pre-check** — see below.
|
|
32
32
|
3. `lightdash upload --apps <appUuid>` (the `appUuid` from this folder's `lightdash-app.yml`) — the **server** rebuilds and serves the app. The server rebuild, not your local build, is what ships.
|
|
33
33
|
|
|
34
34
|
## The local build is optional — never fight a failing install
|
|
35
35
|
|
|
36
|
-
- If `
|
|
36
|
+
- If `npm install` fails (registry policy, an unavailable pinned SDK version, no network access), **skip the local build entirely and go straight to upload**. The server rebuild is authoritative and surfaces build errors on the app page.
|
|
37
37
|
- Do **not** modify machine configuration, `.npmrc` files, registry settings, or the project's dependency files to force an install to work.
|
|
38
38
|
- A missing `node_modules` is a normal state, not a problem to fix. Never run installs just because it is absent.
|
|
39
|
-
- **Exception — adding a dependency** (only on instances with custom dependencies enabled — see "Library boundaries" above).
|
|
40
|
-
- **Never run dependency lifecycle scripts.** The app's `.npmrc` sets `ignore-scripts=true` — leave it. A downloaded app can be authored by someone else, and their dependencies' install scripts must not execute on this machine. Explicit `
|
|
39
|
+
- **Exception — adding a dependency** (only on instances with custom dependencies enabled — see "Library boundaries" above). This is the one workflow that still requires pnpm: upload rejects new dependencies unless `pnpm-lock.yaml` was regenerated to match `package.json`, so dependency resolution MUST succeed locally. Use `pnpm add <pkg>` — prefixed with Socket Firewall when available (`sfw pnpm add <pkg>`; check with `command -v sfw`) to block known-malicious packages — or after editing `package.json` run `pnpm install --lockfile-only` (updates the lockfile without installing). If resolution fails, **stop and report the exact pnpm error to the user** — never hand-edit `package.json` and proceed without the lockfile; the upload will fail.
|
|
40
|
+
- **Never run dependency lifecycle scripts.** The app's `.npmrc` sets `ignore-scripts=true` — leave it. A downloaded app can be authored by someone else, and their dependencies' install scripts must not execute on this machine. Explicit commands such as `npm run build` still work.
|
|
41
|
+
|
|
42
|
+
## Preview locally against real data
|
|
43
|
+
|
|
44
|
+
`lightdash apps preview` (run in this folder) starts a local dev server that renders the app against the Lightdash instance you are logged into, using your CLI credential. Requires `npm install` to have succeeded; if it hasn't, skip preview and rely on the server rebuild.
|
|
45
|
+
|
|
46
|
+
- Preview does not pass your API key to vite or browser code: the CLI holds it behind a loopback proxy that only forwards the SDK route allowlist (query execution, result polling, downloads, current user), pinned to this app's project. No credential is written to the app folder. Never put a real key in `.env.local` or any `VITE_`-prefixed var — anything `VITE_*` is inlined into the page and readable by any code running there.
|
|
47
|
+
- There is no manual `npm run dev` equivalent with data access — bare `npm run dev` starts the page but API calls fail with 401. Always use `lightdash apps preview`.
|
|
48
|
+
- An endpoint that works in preview but not when deployed means it is outside the data-app SDK surface — use the SDK, don't work around the proxy.
|
|
49
|
+
- Declared custom dependencies work in preview too — the dev server bundles whatever the dependency install put in `node_modules`, the same set the server installs on upload.
|
|
50
|
+
- Preview shows **your** data under **your** permissions and user attributes — viewers of the deployed app may see different data. Do not treat preview as verification of viewer-specific behavior.
|
|
51
|
+
- Local preview keeps the credential out of the app environment/browser but is **not a sandbox**: vite and the downloaded tooling execute as your OS user, can read that user's files (including the existing CLI config), and the app can read the query results it requests. Only preview source and dependencies you trust.
|
|
52
|
+
- The dev server applies a CSP that forces API traffic through its same-origin proxy, but `script-src` stays permissive (vite needs it), so preview is **not** a full stand-in for the deployed Content-Security-Policy. A library that works in preview may still be blocked when deployed; the app page after upload is the final check.
|
|
53
|
+
- Host-mediated features are not emulated locally. External connections (`externalFetch`), data-app-viz row/field context, Google Sheets export, the product inspector, and product URL-state integration must be tested after upload.
|
|
41
54
|
|
|
42
55
|
## Project context (read-only reference)
|
|
43
56
|
|
|
@@ -52,4 +65,4 @@ You are editing a Lightdash **data app** that was downloaded with the Lightdash
|
|
|
52
65
|
|
|
53
66
|
Most root config is reference only — editing it has **no effect** because the server rebuilds against its trusted template. This applies to `vite.config.js`, `tailwind.config.js`, `tsconfig.json`, and other build/tooling files.
|
|
54
67
|
|
|
55
|
-
`package.json` is **partially editable** only when custom dependencies are enabled on the Lightdash instance — see "Library boundaries" above; treat it as read-only otherwise. When enabled, you may add npm dependencies with `pnpm add <pkg>` — registry packages with plain semver versions only (no git/file/url specs), up to 60 direct dependencies, and `pnpm-lock.yaml` must be updated alongside (see the exception above — this is the one step that must succeed locally). On upload the CLI warns which packages will be installed in the build sandbox; install scripts never run. Other root config (vite/tailwind/tsconfig) remains read-only.
|
|
68
|
+
`package.json` is **partially editable** only when custom dependencies are enabled on the Lightdash instance — see "Library boundaries" above; treat it as read-only otherwise. When enabled, you may add npm dependencies with `pnpm add <pkg>` — registry packages with plain semver versions only (no git/file/url specs), up to 60 direct dependencies, and `pnpm-lock.yaml` must be updated alongside (see the exception above — this is the one step that still requires pnpm and must succeed locally). On upload the CLI warns which packages will be installed in the build sandbox; install scripts never run. Other root config (vite/tailwind/tsconfig) remains read-only.
|
|
@@ -142,7 +142,38 @@ function jsxSourceLocVitePlugin() {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
export default defineConfig(({ mode }) => {
|
|
145
|
-
|
|
145
|
+
// Load VITE_* (inlined into the browser bundle) AND the preview-only
|
|
146
|
+
// LIGHTDASH_PREVIEW_PROXY_* vars (stay server-side — never inlined, since
|
|
147
|
+
// envPrefix defaults to 'VITE_'). `lightdash apps preview` starts a
|
|
148
|
+
// loopback proxy that holds the real credential and enforces the data-app
|
|
149
|
+
// SDK route allowlist; this dev server only ever learns the proxy's
|
|
150
|
+
// address and a run-scoped nonce. The browser receives that nonce as a
|
|
151
|
+
// project- and route-limited capability, never the durable credential.
|
|
152
|
+
const env = loadEnv(mode, process.cwd(), ['VITE_', 'LIGHTDASH_PREVIEW_']);
|
|
153
|
+
const previewProxyTarget = env.LIGHTDASH_PREVIEW_PROXY_TARGET;
|
|
154
|
+
const previewProxyNonce = env.LIGHTDASH_PREVIEW_PROXY_NONCE;
|
|
155
|
+
const lightdashUrl =
|
|
156
|
+
env.VITE_LIGHTDASH_URL || 'https://app.lightdash.cloud';
|
|
157
|
+
|
|
158
|
+
// Dev-server CSP that emulates the deployed app policy for the vectors that
|
|
159
|
+
// matter for credential/data theft: network egress is locked to same-origin
|
|
160
|
+
// so all API traffic must pass through the allowlisted /api proxy.
|
|
161
|
+
// script-src/style-src stay permissive because Vite's dev runtime needs
|
|
162
|
+
// inline + eval, so this is NOT a full stand-in for the deployed sandbox.
|
|
163
|
+
const previewCsp = [
|
|
164
|
+
`default-src 'none'`,
|
|
165
|
+
`script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:`,
|
|
166
|
+
`style-src 'self' 'unsafe-inline'`,
|
|
167
|
+
`connect-src 'self'`,
|
|
168
|
+
`img-src 'self' data: blob:`,
|
|
169
|
+
`font-src 'self' data:`,
|
|
170
|
+
`worker-src 'self' blob:`,
|
|
171
|
+
`child-src 'self' blob:`,
|
|
172
|
+
`object-src 'none'`,
|
|
173
|
+
`base-uri 'self'`,
|
|
174
|
+
`form-action 'self'`,
|
|
175
|
+
`frame-ancestors 'self'`,
|
|
176
|
+
].join('; ');
|
|
146
177
|
|
|
147
178
|
return {
|
|
148
179
|
plugins: [jsxSourceLocVitePlugin(), react()],
|
|
@@ -153,12 +184,41 @@ export default defineConfig(({ mode }) => {
|
|
|
153
184
|
},
|
|
154
185
|
},
|
|
155
186
|
server: {
|
|
187
|
+
host: '127.0.0.1',
|
|
188
|
+
// Vite otherwise permits every localhost origin. Preview is a
|
|
189
|
+
// credentialed endpoint, so only the page's own origin may read it.
|
|
190
|
+
cors: false,
|
|
191
|
+
headers: { 'Content-Security-Policy': previewCsp },
|
|
156
192
|
proxy: {
|
|
157
193
|
'/api': {
|
|
158
|
-
|
|
159
|
-
|
|
194
|
+
// Under `lightdash apps preview`, /api goes to the CLI's
|
|
195
|
+
// loopback proxy, which checks the route against the
|
|
196
|
+
// data-app SDK allowlist and attaches the credential
|
|
197
|
+
// before forwarding to Lightdash. Without the CLI (bare
|
|
198
|
+
// `npm run dev`) requests go to the instance uncredentialed
|
|
199
|
+
// and fail with 401 — preview requires the CLI command.
|
|
200
|
+
target: previewProxyTarget || lightdashUrl,
|
|
160
201
|
changeOrigin: true,
|
|
161
202
|
secure: true,
|
|
203
|
+
configure: (proxy) => {
|
|
204
|
+
if (!previewProxyNonce) return;
|
|
205
|
+
proxy.on('proxyReq', (proxyReq, req) => {
|
|
206
|
+
// Do not turn Vite into an unauthenticated front
|
|
207
|
+
// door to the nonce-protected CLI proxy. The SDK
|
|
208
|
+
// presents the run-scoped nonce as its local API
|
|
209
|
+
// key; only matching requests receive the inner
|
|
210
|
+
// proxy header.
|
|
211
|
+
if (
|
|
212
|
+
req.headers.authorization ===
|
|
213
|
+
`ApiKey ${previewProxyNonce}`
|
|
214
|
+
) {
|
|
215
|
+
proxyReq.setHeader(
|
|
216
|
+
'x-lightdash-preview-nonce',
|
|
217
|
+
previewProxyNonce,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
},
|
|
162
222
|
},
|
|
163
223
|
},
|
|
164
224
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightdash/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.46.0",
|
|
4
4
|
"description": "Lightdash CLI tool",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"unique-names-generator": "4.7.1",
|
|
42
42
|
"uuid": "11.1.1",
|
|
43
43
|
"yaml": "2.8.3",
|
|
44
|
-
"@lightdash/
|
|
45
|
-
"@lightdash/
|
|
44
|
+
"@lightdash/warehouses": "1.46.0",
|
|
45
|
+
"@lightdash/common": "1.46.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/inquirer": "8.2.4",
|