@cognite/dune 0.1.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.
@@ -0,0 +1,38 @@
1
+ ---
2
+ to: <%= name %>/src/App.test.tsx
3
+ ---
4
+ import * as dune from "@cognite/dune";
5
+ import { render, screen } from "@testing-library/react";
6
+ import { beforeEach, describe, expect, it, vi } from "vitest";
7
+ import App from "./App";
8
+
9
+ // Mock the @cognite/dune module
10
+ vi.mock(import("@cognite/dune"));
11
+
12
+ describe("App", () => {
13
+ beforeEach(() => {
14
+ vi.clearAllMocks();
15
+ });
16
+
17
+ it("renders loading state", () => {
18
+ vi.mocked(dune.useDune).mockReturnValue({
19
+ sdk: { project: "test-project" } as any,
20
+ isLoading: true,
21
+ });
22
+
23
+ render(<App />);
24
+ expect(screen.getByText("Loading...")).toBeInTheDocument();
25
+ });
26
+
27
+ it("renders app with project name", () => {
28
+ vi.mocked(dune.useDune).mockReturnValue({
29
+ sdk: { project: "my-test-project" } as any,
30
+ isLoading: false,
31
+ });
32
+
33
+ render(<App />);
34
+ expect(screen.getByText("<%= displayName %>")).toBeInTheDocument();
35
+ expect(screen.getByText("Project: my-test-project")).toBeInTheDocument();
36
+ });
37
+ });
38
+
@@ -0,0 +1,23 @@
1
+ ---
2
+ to: <%= name %>/src/App.tsx
3
+ ---
4
+ import { useDune } from "@cognite/dune";
5
+
6
+ function App() {
7
+ const { sdk, isLoading } = useDune();
8
+ const project = sdk.project;
9
+
10
+ if (isLoading) {
11
+ return <div>Loading...</div>;
12
+ }
13
+
14
+ return (
15
+ <div>
16
+ <h1><%= displayName %></h1>
17
+ <p>Project: {project}</p>
18
+ </div>
19
+ );
20
+ }
21
+
22
+ export default App;
23
+
@@ -0,0 +1,20 @@
1
+ ---
2
+ to: <%= name %>/app.json
3
+ ---
4
+ {
5
+ "name": "<%= displayName %>",
6
+ "description": "<%= description %>",
7
+ "externalId": "<%= name %>",
8
+ "versionTag": "0.0.1",
9
+ "deployments": [
10
+ {
11
+ "org": "<%= org %>",
12
+ "project": "<%= project %>",
13
+ "baseUrl": "https://<%= cluster %>.cognitedata.com",
14
+ "published": false,
15
+ "deployClientId": "",
16
+ "deploySecretName": "<%= org %>_<%= project %>_<%= cluster %>"
17
+ }
18
+ ]
19
+ }
20
+
@@ -0,0 +1,25 @@
1
+ ---
2
+ to: <%= name %>/biome.json
3
+ ---
4
+ {
5
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
6
+ "vcs": {
7
+ "enabled": true,
8
+ "clientKind": "git",
9
+ "useIgnoreFile": true
10
+ },
11
+ "organizeImports": {
12
+ "enabled": true
13
+ },
14
+ "linter": {
15
+ "enabled": true,
16
+ "rules": {
17
+ "recommended": true
18
+ }
19
+ },
20
+ "formatter": {
21
+ "enabled": true,
22
+ "indentStyle": "tab"
23
+ }
24
+ }
25
+
@@ -0,0 +1,21 @@
1
+ ---
2
+ to: <%= name %>/.gitignore
3
+ ---
4
+ # Dependencies
5
+ node_modules
6
+
7
+ # Build output
8
+ dist
9
+
10
+ # Environment variables
11
+ .env
12
+ .env.local
13
+
14
+ # Editor directories
15
+ .vscode
16
+ .idea
17
+
18
+ # OS files
19
+ .DS_Store
20
+ Thumbs.db
21
+
@@ -0,0 +1,36 @@
1
+ ---
2
+ to: <%= name %>/index.html
3
+ ---
4
+ <!DOCTYPE html>
5
+ <html lang="en">
6
+ <head>
7
+ <meta charset="UTF-8" />
8
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
9
+ <meta
10
+ name="viewport"
11
+ content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
12
+ />
13
+ <meta name="theme-color" content="#000000" />
14
+ <meta name="description" content="Cognite Data Fusion Application" />
15
+ <meta name="apple-mobile-web-app-capable" content="yes" />
16
+ <meta name="apple-mobile-web-app-status-bar-style" content="default" />
17
+ <meta name="apple-mobile-web-app-title" content="CDF Application" />
18
+ <title><%= displayName %></title>
19
+
20
+ <!-- PWA Manifest -->
21
+ <link rel="manifest" href="/manifest.json" />
22
+
23
+ <!-- Fonts -->
24
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
25
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
26
+ <link
27
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Source+Code+Pro:wght@500&display=swap"
28
+ rel="stylesheet"
29
+ />
30
+ </head>
31
+ <body>
32
+ <div id="root"></div>
33
+ <script type="module" src="/src/main.tsx"></script>
34
+ </body>
35
+ </html>
36
+
@@ -0,0 +1,28 @@
1
+ ---
2
+ to: <%= name %>/src/main.tsx
3
+ ---
4
+ import { DuneAuthProvider } from "@cognite/dune";
5
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
6
+ import React from "react";
7
+ import ReactDOM from "react-dom/client";
8
+ import App from "./App.tsx";
9
+
10
+ const queryClient = new QueryClient({
11
+ defaultOptions: {
12
+ queries: {
13
+ staleTime: 5 * 60 * 1000, // 5 minutes
14
+ gcTime: 10 * 60 * 1000, // 10 minutes
15
+ },
16
+ },
17
+ });
18
+
19
+ ReactDOM.createRoot(document.getElementById("root")!).render(
20
+ <React.StrictMode>
21
+ <QueryClientProvider client={queryClient}>
22
+ <DuneAuthProvider>
23
+ <App />
24
+ </DuneAuthProvider>
25
+ </QueryClientProvider>
26
+ </React.StrictMode>
27
+ );
28
+
@@ -0,0 +1,44 @@
1
+ ---
2
+ to: <%= name %>/package.json
3
+ ---
4
+ {
5
+ "name": "<%= name %>",
6
+ "version": "0.0.0",
7
+ "private": true,
8
+ "type": "module",
9
+ "scripts": {
10
+ "start": "vite",
11
+ "dev": "vite",
12
+ "build": "tsc && vite build",
13
+ "preview": "vite preview",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest",
16
+ "test:ui": "vitest --ui",
17
+ "lint": "biome check .",
18
+ "lint:fix": "biome check --write ."
19
+ },
20
+ "dependencies": {
21
+ "@cognite/sdk": "^10.3.0",
22
+ "@cognite/dune": "^0.1.0",
23
+ "@tanstack/react-query": "^5.90.10",
24
+ "react": "^19.2.0",
25
+ "react-dom": "^19.2.0"
26
+ },
27
+ "devDependencies": {
28
+ "@biomejs/biome": "^1.9.4",
29
+ "@testing-library/jest-dom": "^6.6.3",
30
+ "@testing-library/react": "^16.1.0",
31
+ "@testing-library/user-event": "^14.5.2",
32
+ "@types/node": "^24.10.1",
33
+ "@types/react": "^19.2.6",
34
+ "@types/react-dom": "^19.2.3",
35
+ "@vitejs/plugin-react": "^5.1.1",
36
+ "@vitest/ui": "^2.1.8",
37
+ "happy-dom": "^20.0.2",
38
+ "typescript": "^5.0.0",
39
+ "vite": "^7.2.4",
40
+ "vite-plugin-mkcert": "^1.17.9",
41
+ "vitest": "^2.1.8"
42
+ }
43
+ }
44
+
@@ -0,0 +1,57 @@
1
+ module.exports = [
2
+ {
3
+ type: "input",
4
+ name: "name",
5
+ message: "What is the app name? (use kebab-case, e.g., my-awesome-app)",
6
+ initial: "my-dune-app",
7
+ validate: (input) => {
8
+ if (!input) return "App name is required";
9
+ if (!/^[a-z][a-z0-9-]*$/.test(input)) {
10
+ return "App name must be kebab-case (lowercase, hyphens only)";
11
+ }
12
+ return true;
13
+ },
14
+ },
15
+ {
16
+ type: "input",
17
+ name: "displayName",
18
+ message: "What is the display name? (e.g., My Awesome App)",
19
+ initial: "My Dune app",
20
+ },
21
+ {
22
+ type: "input",
23
+ name: "description",
24
+ message: "What is the app description?",
25
+ initial: "A Dune application",
26
+ },
27
+ {
28
+ type: "input",
29
+ name: "org",
30
+ message: "Deployment org? (e.g., cog-demo, cog-atlas)",
31
+ initial: "cog-demo",
32
+ validate: (input) => {
33
+ if (!input) return "Org is required";
34
+ return true;
35
+ },
36
+ },
37
+ {
38
+ type: "input",
39
+ name: "project",
40
+ message: "Deployment project? (e.g., lervik-industries, atlas-greenfield)",
41
+ initial: "lervik-industries",
42
+ validate: (input) => {
43
+ if (!input) return "Project is required";
44
+ return true;
45
+ },
46
+ },
47
+ {
48
+ type: "input",
49
+ name: "cluster",
50
+ message: "Cluster/baseUrl? (e.g., greenfield, westeurope-1, api)",
51
+ initial: "api",
52
+ validate: (input) => {
53
+ if (!input) return "Cluster is required";
54
+ return true;
55
+ },
56
+ },
57
+ ];
@@ -0,0 +1,33 @@
1
+ ---
2
+ to: <%= name %>/tsconfig.json
3
+ ---
4
+ {
5
+ "compilerOptions": {
6
+ "target": "ES2020",
7
+ "useDefineForClassFields": true,
8
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
9
+ "module": "ESNext",
10
+ "skipLibCheck": true,
11
+
12
+ /* Bundler mode */
13
+ "moduleResolution": "bundler",
14
+ "allowImportingTsExtensions": true,
15
+ "isolatedModules": true,
16
+ "moduleDetection": "force",
17
+ "noEmit": true,
18
+ "jsx": "react-jsx",
19
+
20
+ /* Linting */
21
+ "strict": true,
22
+ "noUnusedLocals": true,
23
+ "noUnusedParameters": true,
24
+ "noFallthroughCasesInSwitch": true,
25
+
26
+ /* Testing */
27
+ "types": ["vitest/globals", "@testing-library/jest-dom"],
28
+ "baseUrl": "."
29
+ },
30
+ "include": ["src"],
31
+ "references": [{ "path": "./tsconfig.node.json" }]
32
+ }
33
+
@@ -0,0 +1,26 @@
1
+ ---
2
+ to: <%= name %>/tsconfig.node.json
3
+ ---
4
+ {
5
+ "compilerOptions": {
6
+ "target": "ES2022",
7
+ "lib": ["ES2023"],
8
+ "module": "ESNext",
9
+ "skipLibCheck": true,
10
+
11
+ /* Bundler mode */
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "isolatedModules": true,
15
+ "moduleDetection": "force",
16
+ "noEmit": true,
17
+
18
+ /* Linting */
19
+ "strict": true,
20
+ "noUnusedLocals": true,
21
+ "noUnusedParameters": true,
22
+ "noFallthroughCasesInSwitch": true
23
+ },
24
+ "include": ["vite.config.ts", "vitest.config.ts"]
25
+ }
26
+
@@ -0,0 +1,15 @@
1
+ ---
2
+ to: <%= name %>/vite.config.ts
3
+ ---
4
+ import react from "@vitejs/plugin-react";
5
+ import { defineConfig } from "vite";
6
+ import mkcert from "vite-plugin-mkcert";
7
+
8
+ export default defineConfig({
9
+ plugins: [react(), mkcert()],
10
+ server: {
11
+ https: true,
12
+ port: 3000,
13
+ },
14
+ });
15
+
@@ -0,0 +1,15 @@
1
+ ---
2
+ to: <%= name %>/vitest.config.ts
3
+ ---
4
+ import react from "@vitejs/plugin-react";
5
+ import { defineConfig } from "vitest/config";
6
+
7
+ export default defineConfig({
8
+ plugins: [react()],
9
+ test: {
10
+ globals: true,
11
+ environment: "happy-dom",
12
+ setupFiles: ["./vitest.setup.ts"],
13
+ },
14
+ });
15
+
@@ -0,0 +1,5 @@
1
+ ---
2
+ to: <%= name %>/vitest.setup.ts
3
+ ---
4
+ import "@testing-library/jest-dom/vitest";
5
+
package/bin/cli.js ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { dirname, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { Logger, runner } from "hygen";
6
+
7
+ const defaultTemplates = resolve(dirname(fileURLToPath(import.meta.url)), "..", "_templates");
8
+
9
+ async function main() {
10
+ const args = process.argv.slice(2);
11
+ const command = args[0];
12
+
13
+ // Handle different commands
14
+ if (command === "create" || command === "new" || !command) {
15
+ // Run the app generator
16
+ const hygenArgs = ["app", "new"];
17
+
18
+ try {
19
+ const enquirer = await import("enquirer");
20
+
21
+ await runner(hygenArgs, {
22
+ templates: defaultTemplates,
23
+ cwd: process.cwd(),
24
+ logger: new Logger(console.log.bind(console)),
25
+ createPrompter: () => enquirer.default,
26
+ exec: async (action, body) => {
27
+ const { execa } = await import("execa");
28
+ const opts = body && body.length > 0 ? { input: body } : {};
29
+ return execa(action, { shell: true, ...opts });
30
+ },
31
+ debug: !!process.env.DEBUG,
32
+ });
33
+ } catch (error) {
34
+ console.error("Error:", error.message);
35
+ process.exit(1);
36
+ }
37
+ } else if (command === "help" || command === "--help" || command === "-h") {
38
+ console.log(`
39
+ @cognite/dune - Build and deploy React apps to Cognite Data Fusion
40
+
41
+ Usage:
42
+ npx @cognite/dune [command]
43
+
44
+ Commands:
45
+ create, new Create a new Dune application (default)
46
+ help Show this help message
47
+
48
+ Examples:
49
+ npx @cognite/dune # Create a new app (interactive)
50
+ npx @cognite/dune create # Create a new app (interactive)
51
+
52
+ For programmatic usage:
53
+ import { DuneAuthProvider, useDune } from "@cognite/dune/auth"
54
+ import { deploy } from "@cognite/dune/deploy"
55
+ import { fusionOpenPlugin } from "@cognite/dune/vite"
56
+ `);
57
+ } else {
58
+ console.error(`Unknown command: ${command}`);
59
+ console.log('Run "npx @cognite/dune help" for usage information.');
60
+ process.exit(1);
61
+ }
62
+ }
63
+
64
+ main();
@@ -0,0 +1,41 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as _cognite_sdk from '@cognite/sdk';
3
+ import { CogniteClient } from '@cognite/sdk';
4
+ import React from 'react';
5
+
6
+ declare const DuneAuthProviderContext: React.Context<{
7
+ sdk: CogniteClient;
8
+ isLoading: boolean;
9
+ error?: string;
10
+ }>;
11
+ interface DuneAuthProviderProps {
12
+ children: React.ReactNode;
13
+ useIFrameAuthentication?: boolean;
14
+ useLocalConfiguration?: {
15
+ org: string;
16
+ project: string;
17
+ baseUrl: string;
18
+ };
19
+ loadingComponent?: React.ReactNode;
20
+ errorComponent?: (error: string) => React.ReactNode;
21
+ }
22
+ declare const DuneAuthProvider: ({ children, loadingComponent, errorComponent, }: DuneAuthProviderProps) => react_jsx_runtime.JSX.Element;
23
+
24
+ declare const useDune: () => {
25
+ sdk: _cognite_sdk.CogniteClient;
26
+ isLoading: boolean;
27
+ error?: string;
28
+ };
29
+
30
+ interface CDFConfig {
31
+ project: string;
32
+ baseUrl: string;
33
+ clientId: string;
34
+ clientSecret: string;
35
+ appId?: string;
36
+ }
37
+ declare const getToken: (clientId: string, clientSecret: string) => Promise<any>;
38
+ declare const createCDFSDK: (config: CDFConfig) => Promise<CogniteClient>;
39
+ declare const EMPTY_SDK: CogniteClient;
40
+
41
+ export { type CDFConfig, DuneAuthProvider, DuneAuthProviderContext, type DuneAuthProviderProps, EMPTY_SDK, createCDFSDK, getToken, useDune };
@@ -0,0 +1,16 @@
1
+ import {
2
+ DuneAuthProvider,
3
+ DuneAuthProviderContext,
4
+ EMPTY_SDK,
5
+ createCDFSDK,
6
+ getToken,
7
+ useDune
8
+ } from "../chunk-VIBN7U5H.js";
9
+ export {
10
+ DuneAuthProvider,
11
+ DuneAuthProviderContext,
12
+ EMPTY_SDK,
13
+ createCDFSDK,
14
+ getToken,
15
+ useDune
16
+ };
@@ -0,0 +1,152 @@
1
+ // src/auth/dune-auth-provider.tsx
2
+ import { CogniteClient as CogniteClient2 } from "@cognite/sdk";
3
+ import { createContext, useEffect } from "react";
4
+ import { useState } from "react";
5
+
6
+ // src/auth/utils.ts
7
+ import { CogniteClient } from "@cognite/sdk";
8
+ var getToken = async (clientId, clientSecret) => {
9
+ const header = `Basic ${btoa(`${clientId}:${clientSecret}`)}`;
10
+ const response = await fetch("https://auth.cognite.com/oauth2/token", {
11
+ method: "POST",
12
+ headers: {
13
+ Authorization: header,
14
+ "Content-Type": "application/x-www-form-urlencoded"
15
+ },
16
+ body: new URLSearchParams({
17
+ grant_type: "client_credentials"
18
+ })
19
+ });
20
+ const data = await response.json();
21
+ return data.access_token;
22
+ };
23
+ var createCDFSDK = async (config) => {
24
+ const { project, baseUrl, clientId, clientSecret, appId = "cdf-authentication-package" } = config;
25
+ if (!project || !baseUrl || !clientId || !clientSecret) {
26
+ throw new Error(
27
+ "Missing required configuration. Please provide: project, baseUrl, clientId, clientSecret"
28
+ );
29
+ }
30
+ const sdk = new CogniteClient({
31
+ appId,
32
+ project,
33
+ baseUrl,
34
+ oidcTokenProvider: async () => {
35
+ return await getToken(clientId, clientSecret);
36
+ }
37
+ });
38
+ await sdk.authenticate();
39
+ return sdk;
40
+ };
41
+ var EMPTY_SDK = new CogniteClient({
42
+ appId: "cdf-authentication-package",
43
+ project: "",
44
+ oidcTokenProvider: async () => ""
45
+ });
46
+ var requestCredentials = () => {
47
+ console.log("\u{1F511} Requesting credentials from parent...");
48
+ if (window.parent && window.parent !== window) {
49
+ window.parent.postMessage({ type: "REQUEST_CREDENTIALS" }, "*");
50
+ }
51
+ };
52
+ var handleCredentialsResponse = (event) => {
53
+ if (event.data?.type === "PROVIDE_CREDENTIALS" && event.data?.credentials) {
54
+ const creds = event.data.credentials;
55
+ if (creds.token && creds.baseUrl && creds.project) {
56
+ console.log("\u{1F389} useCredentials received credentials:", {
57
+ hasToken: !!creds.token,
58
+ tokenLength: creds.token?.length || 0,
59
+ project: creds.project,
60
+ baseUrl: creds.baseUrl
61
+ });
62
+ return creds;
63
+ }
64
+ }
65
+ };
66
+
67
+ // src/auth/dune-auth-provider.tsx
68
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
69
+ var DuneAuthProviderContext = createContext({
70
+ sdk: EMPTY_SDK,
71
+ isLoading: false
72
+ });
73
+ var DuneAuthProvider = ({
74
+ children,
75
+ loadingComponent = /* @__PURE__ */ jsx("div", { children: "Loading CDF authentication..." }),
76
+ errorComponent = (error) => /* @__PURE__ */ jsxs("div", { children: [
77
+ "Authentication error: ",
78
+ error
79
+ ] })
80
+ }) => {
81
+ return /* @__PURE__ */ jsx(
82
+ FusionIframeAuthenticationInnerProvider,
83
+ {
84
+ loadingComponent,
85
+ errorComponent,
86
+ children
87
+ }
88
+ );
89
+ };
90
+ var FusionIframeAuthenticationInnerProvider = ({
91
+ children,
92
+ loadingComponent = /* @__PURE__ */ jsx("div", { children: "Loading CDF authentication..." }),
93
+ errorComponent = (error) => /* @__PURE__ */ jsxs("div", { children: [
94
+ "Authentication error: ",
95
+ error
96
+ ] })
97
+ }) => {
98
+ const [isLoading, setIsLoading] = useState(true);
99
+ const [sdk, setSdk] = useState(EMPTY_SDK);
100
+ const [error] = useState();
101
+ useEffect(() => {
102
+ requestCredentials();
103
+ const handleMessage = async (event) => {
104
+ console.log("\u{1F50D} Handling message from Fusion");
105
+ const credentials = handleCredentialsResponse(event);
106
+ if (!credentials) {
107
+ return;
108
+ }
109
+ const sdk2 = new CogniteClient2({
110
+ appId: "dune-app",
111
+ project: credentials.project,
112
+ baseUrl: credentials.baseUrl,
113
+ oidcTokenProvider: async () => {
114
+ return credentials.token;
115
+ }
116
+ });
117
+ await sdk2.authenticate();
118
+ setSdk(sdk2);
119
+ setIsLoading(false);
120
+ };
121
+ console.log("\u{1F50D} Adding message listener");
122
+ window.addEventListener("message", handleMessage);
123
+ return () => window.removeEventListener("message", handleMessage);
124
+ }, []);
125
+ console.log("\u{1F50D} CDFIframeAuthenticationInnerProvider", sdk, isLoading, error);
126
+ if (error && errorComponent) {
127
+ return /* @__PURE__ */ jsx(Fragment, { children: errorComponent(error) });
128
+ }
129
+ if (isLoading && loadingComponent) {
130
+ return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent });
131
+ }
132
+ return /* @__PURE__ */ jsx(DuneAuthProviderContext.Provider, { value: { sdk, isLoading, error }, children });
133
+ };
134
+
135
+ // src/auth/use-dune.ts
136
+ import { useContext } from "react";
137
+ var useDune = () => {
138
+ const context = useContext(DuneAuthProviderContext);
139
+ if (!context) {
140
+ throw new Error("useDune must be used within a DuneAuthProvider");
141
+ }
142
+ return context;
143
+ };
144
+
145
+ export {
146
+ getToken,
147
+ createCDFSDK,
148
+ EMPTY_SDK,
149
+ DuneAuthProviderContext,
150
+ DuneAuthProvider,
151
+ useDune
152
+ };
@@ -0,0 +1,83 @@
1
+ import { CogniteClient } from '@cognite/sdk';
2
+
3
+ type Deployment = {
4
+ org: string;
5
+ project: string;
6
+ baseUrl: string;
7
+ deployClientId: string;
8
+ deploySecretName: string;
9
+ published: boolean;
10
+ };
11
+ type App = {
12
+ externalId: string;
13
+ name: string;
14
+ description: string;
15
+ versionTag: string;
16
+ };
17
+
18
+ declare const deploy: (deployment: Deployment, app: App, folder: string) => Promise<void>;
19
+
20
+ /**
21
+ * CDF Application Deployment
22
+ *
23
+ * Handles deployment of packaged applications to CDF.
24
+ */
25
+
26
+ declare class CdfApplicationDeployer {
27
+ private client;
28
+ /**
29
+ * @param {CogniteClient} client - Cognite SDK client
30
+ */
31
+ constructor(client: CogniteClient);
32
+ /**
33
+ * Upload application package to CDF Files API
34
+ * @param {string} appExternalId - Application external ID
35
+ * @param {string} name - Application name
36
+ * @param {string} description - Application description
37
+ * @param {string} versionTag - Version tag
38
+ * @param {string} zipFilename - Path to zip file
39
+ * @param {boolean} published - Whether the application should be published
40
+ */
41
+ uploadToFilesApi(appExternalId: string, name: string, description: string, versionTag: string, zipFilename: string, published?: boolean): Promise<void>;
42
+ /**
43
+ * Execute complete deployment to CDF
44
+ * @param {string} appExternalId - Application external ID
45
+ * @param {string} name - Application name
46
+ * @param {string} description - Application description
47
+ * @param {string} versionTag - Version tag
48
+ * @param {string} zipFilename - Path to zip file
49
+ * @param {boolean} published - Whether the application should be published
50
+ */
51
+ deploy(appExternalId: string, name: string, description: string, versionTag: string, zipFilename: string, published?: boolean): Promise<void>;
52
+ }
53
+
54
+ /**
55
+ * Application Packaging
56
+ *
57
+ * Handles packaging of build directories into deployment-ready zip files.
58
+ */
59
+ declare class ApplicationPackager {
60
+ private distPath;
61
+ /**
62
+ * @param {string} distDirectory - Build directory to package (can be relative or absolute)
63
+ */
64
+ constructor(distDirectory?: string);
65
+ /**
66
+ * Validate that build directory exists
67
+ * @throws {Error} If build directory not found
68
+ */
69
+ validateBuildDirectory(): void;
70
+ /**
71
+ * Create zip file from build directory
72
+ * @param {string} outputFilename - Output zip filename
73
+ * @param {boolean} verbose - Enable verbose logging
74
+ * @returns {Promise<string>} Path to created zip file
75
+ */
76
+ createZip(outputFilename?: string, verbose?: boolean): Promise<string>;
77
+ }
78
+
79
+ declare const getSdk: (deployment: Deployment, folder: string) => Promise<CogniteClient>;
80
+
81
+ declare const getToken: (deployClientId: string, deploySecretName: string) => Promise<any>;
82
+
83
+ export { type App, ApplicationPackager, CdfApplicationDeployer, type Deployment, deploy, getSdk, getToken };
@@ -0,0 +1,219 @@
1
+ // src/deploy/application-deployer.ts
2
+ import fs from "fs";
3
+ var CdfApplicationDeployer = class {
4
+ /**
5
+ * @param {CogniteClient} client - Cognite SDK client
6
+ */
7
+ constructor(client) {
8
+ this.client = client;
9
+ }
10
+ /**
11
+ * Upload application package to CDF Files API
12
+ * @param {string} appExternalId - Application external ID
13
+ * @param {string} name - Application name
14
+ * @param {string} description - Application description
15
+ * @param {string} versionTag - Version tag
16
+ * @param {string} zipFilename - Path to zip file
17
+ * @param {boolean} published - Whether the application should be published
18
+ */
19
+ async uploadToFilesApi(appExternalId, name, description, versionTag, zipFilename, published = false) {
20
+ console.log("\u{1F4C1} Creating file record...");
21
+ const fileContent = fs.readFileSync(zipFilename);
22
+ const metadata = {
23
+ published: String(published),
24
+ name,
25
+ description,
26
+ externalId: `${appExternalId}-${versionTag}`,
27
+ version: versionTag
28
+ };
29
+ await this.client.files.upload(
30
+ {
31
+ name: `${appExternalId}-${versionTag}.zip`,
32
+ externalId: `${appExternalId}-${versionTag}`,
33
+ directory: "/dune-apps",
34
+ metadata
35
+ },
36
+ fileContent,
37
+ true,
38
+ // overwrite
39
+ true
40
+ // waitUntilAcknowledged
41
+ );
42
+ console.log("\u2705 File record created");
43
+ }
44
+ /**
45
+ * Execute complete deployment to CDF
46
+ * @param {string} appExternalId - Application external ID
47
+ * @param {string} name - Application name
48
+ * @param {string} description - Application description
49
+ * @param {string} versionTag - Version tag
50
+ * @param {string} zipFilename - Path to zip file
51
+ * @param {boolean} published - Whether the application should be published
52
+ */
53
+ async deploy(appExternalId, name, description, versionTag, zipFilename, published = false) {
54
+ console.log("\n\u{1F680} Deploying application to CDF...\n");
55
+ try {
56
+ await this.uploadToFilesApi(
57
+ appExternalId,
58
+ name,
59
+ description,
60
+ versionTag,
61
+ zipFilename,
62
+ published
63
+ );
64
+ console.log("\n\u2705 Deployment successful!");
65
+ } catch (error) {
66
+ const message = error instanceof Error ? error.message : String(error);
67
+ throw new Error(`Deployment failed: ${message}`);
68
+ }
69
+ }
70
+ };
71
+
72
+ // src/deploy/application-packager.ts
73
+ import fs2 from "fs";
74
+ import path from "path";
75
+ import archiver from "archiver";
76
+ var ApplicationPackager = class {
77
+ /**
78
+ * @param {string} distDirectory - Build directory to package (can be relative or absolute)
79
+ */
80
+ constructor(distDirectory = "dist") {
81
+ this.distPath = path.isAbsolute(distDirectory) ? distDirectory : path.join(process.cwd(), distDirectory);
82
+ }
83
+ /**
84
+ * Validate that build directory exists
85
+ * @throws {Error} If build directory not found
86
+ */
87
+ validateBuildDirectory() {
88
+ if (!fs2.existsSync(this.distPath)) {
89
+ throw new Error(`Build directory "${this.distPath}" not found. Run build first.`);
90
+ }
91
+ }
92
+ /**
93
+ * Create zip file from build directory
94
+ * @param {string} outputFilename - Output zip filename
95
+ * @param {boolean} verbose - Enable verbose logging
96
+ * @returns {Promise<string>} Path to created zip file
97
+ */
98
+ async createZip(outputFilename = "app.zip", verbose = false) {
99
+ this.validateBuildDirectory();
100
+ console.log("\u{1F4E6} Packaging application...");
101
+ return new Promise((resolve, reject) => {
102
+ const output = fs2.createWriteStream(outputFilename);
103
+ const archive = archiver("zip", {
104
+ zlib: { level: 9 }
105
+ // Maximum compression
106
+ });
107
+ output.on("close", () => {
108
+ const sizeMB = (archive.pointer() / 1024 / 1024).toFixed(2);
109
+ console.log(`\u2705 App packaged: ${outputFilename} (${sizeMB} MB)`);
110
+ resolve(outputFilename);
111
+ });
112
+ archive.on("error", (err) => {
113
+ reject(new Error(`Failed to create zip: ${err.message}`));
114
+ });
115
+ if (verbose) {
116
+ archive.on("entry", (entry) => {
117
+ console.log(` \u{1F4C4} ${entry.name}`);
118
+ });
119
+ }
120
+ archive.pipe(output);
121
+ archive.directory(this.distPath, false);
122
+ archive.finalize();
123
+ });
124
+ }
125
+ };
126
+
127
+ // src/deploy/get-sdk.ts
128
+ import { CogniteClient } from "@cognite/sdk";
129
+
130
+ // src/deploy/login.ts
131
+ var loadSecretsFromEnv = () => {
132
+ const secretsJson = process.env.DEPLOYMENT_SECRETS;
133
+ if (!secretsJson) {
134
+ return {};
135
+ }
136
+ try {
137
+ const secrets = JSON.parse(secretsJson);
138
+ const normalizedSecrets = {};
139
+ for (const [key, value] of Object.entries(secrets)) {
140
+ if (typeof value === "string") {
141
+ const normalizedKey = key.toLowerCase().replace(/_/g, "-");
142
+ normalizedSecrets[normalizedKey] = value;
143
+ }
144
+ }
145
+ return normalizedSecrets;
146
+ } catch (error) {
147
+ console.error("Error parsing DEPLOYMENT_SECRETS:", error);
148
+ return {};
149
+ }
150
+ };
151
+ var getToken = async (deployClientId, deploySecretName) => {
152
+ let deploySecret;
153
+ if (process.env.DEPLOYMENT_SECRET) {
154
+ deploySecret = process.env.DEPLOYMENT_SECRET;
155
+ }
156
+ if (!deploySecret) {
157
+ const secrets = loadSecretsFromEnv();
158
+ deploySecret = secrets[deploySecretName];
159
+ }
160
+ if (!deploySecret) {
161
+ deploySecret = process.env[deploySecretName];
162
+ }
163
+ if (!deploySecret) {
164
+ throw new Error(`Deployment secret not found in environment: ${deploySecretName}`);
165
+ }
166
+ const header = `Basic ${btoa(`${deployClientId}:${deploySecret}`)}`;
167
+ const response = await fetch("https://auth.cognite.com/oauth2/token", {
168
+ method: "POST",
169
+ headers: {
170
+ Authorization: header,
171
+ "Content-Type": "application/x-www-form-urlencoded"
172
+ },
173
+ body: new URLSearchParams({ grant_type: "client_credentials" })
174
+ });
175
+ if (!response.ok) {
176
+ throw new Error(`Failed to get token: ${response.status} ${response.statusText}`);
177
+ }
178
+ const data = await response.json();
179
+ return data.access_token;
180
+ };
181
+
182
+ // src/deploy/get-sdk.ts
183
+ var getSdk = async (deployment, folder) => {
184
+ const token = await getToken(deployment.deployClientId, deployment.deploySecretName);
185
+ const sdk = new CogniteClient({
186
+ appId: folder,
187
+ project: deployment.project,
188
+ baseUrl: deployment.baseUrl,
189
+ oidcTokenProvider: async () => {
190
+ return token;
191
+ }
192
+ });
193
+ await sdk.authenticate();
194
+ return sdk;
195
+ };
196
+
197
+ // src/deploy/deploy.ts
198
+ var deploy = async (deployment, app, folder) => {
199
+ const sdk = await getSdk(deployment, folder);
200
+ const distPath = `${folder}/dist`;
201
+ const packager = new ApplicationPackager(distPath);
202
+ const zipFilename = await packager.createZip("app.zip", true);
203
+ const deployer = new CdfApplicationDeployer(sdk);
204
+ await deployer.deploy(
205
+ app.externalId,
206
+ app.name,
207
+ app.description,
208
+ app.versionTag,
209
+ zipFilename,
210
+ deployment.published
211
+ );
212
+ };
213
+ export {
214
+ ApplicationPackager,
215
+ CdfApplicationDeployer,
216
+ deploy,
217
+ getSdk,
218
+ getToken
219
+ };
@@ -0,0 +1,4 @@
1
+ export { CDFConfig, DuneAuthProvider, DuneAuthProviderContext, DuneAuthProviderProps, EMPTY_SDK, createCDFSDK, getToken, useDune } from './auth/index.js';
2
+ import 'react/jsx-runtime';
3
+ import '@cognite/sdk';
4
+ import 'react';
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ import {
2
+ DuneAuthProvider,
3
+ DuneAuthProviderContext,
4
+ EMPTY_SDK,
5
+ createCDFSDK,
6
+ getToken,
7
+ useDune
8
+ } from "./chunk-VIBN7U5H.js";
9
+ export {
10
+ DuneAuthProvider,
11
+ DuneAuthProviderContext,
12
+ EMPTY_SDK,
13
+ createCDFSDK,
14
+ getToken,
15
+ useDune
16
+ };
@@ -0,0 +1,14 @@
1
+ interface ViteDevServer {
2
+ httpServer?: {
3
+ address: () => {
4
+ port: number;
5
+ } | string | null;
6
+ on: (event: string, callback: () => void) => void;
7
+ } | null;
8
+ }
9
+ declare const fusionOpenPlugin: () => {
10
+ name: string;
11
+ configureServer(server: ViteDevServer): void;
12
+ };
13
+
14
+ export { fusionOpenPlugin };
@@ -0,0 +1,41 @@
1
+ // src/vite/fusion-open-plugin.ts
2
+ import { exec } from "child_process";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ var openUrl = (url) => {
6
+ const start = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
7
+ if (process.platform === "win32") {
8
+ exec(`start "" "${url}"`);
9
+ } else {
10
+ exec(`${start} "${url}"`);
11
+ }
12
+ };
13
+ var fusionOpenPlugin = () => {
14
+ return {
15
+ name: "fusion-open",
16
+ configureServer(server) {
17
+ server.httpServer?.on("listening", () => {
18
+ const address = server.httpServer?.address();
19
+ const port = address && typeof address === "object" ? address.port : 3e3;
20
+ const appJsonPath = path.join(process.cwd(), "app.json");
21
+ if (fs.existsSync(appJsonPath)) {
22
+ try {
23
+ const appJson = JSON.parse(fs.readFileSync(appJsonPath, "utf-8"));
24
+ const firstDeployment = appJson.deployments?.[0];
25
+ const { org, project, baseUrl } = firstDeployment || {};
26
+ const parsedBaseUrl = baseUrl?.split("//")[1];
27
+ if (org && project && baseUrl) {
28
+ const fusionUrl = `https://${org}.fusion.cognite.com/${project}/streamlit-apps/dune/development/${port}?cluster=${parsedBaseUrl}&workspace=industrial-tools`;
29
+ openUrl(fusionUrl);
30
+ }
31
+ } catch (error) {
32
+ console.warn("Failed to read app.json for Fusion URL", error);
33
+ }
34
+ }
35
+ });
36
+ }
37
+ };
38
+ };
39
+ export {
40
+ fusionOpenPlugin
41
+ };
package/package.json ADDED
@@ -0,0 +1,93 @@
1
+ {
2
+ "name": "@cognite/dune",
3
+ "version": "0.1.0",
4
+ "description": "Build and deploy React apps to Cognite Data Fusion",
5
+ "keywords": [
6
+ "cognite",
7
+ "dune",
8
+ "cdf",
9
+ "fusion",
10
+ "react",
11
+ "scaffold",
12
+ "deploy"
13
+ ],
14
+ "license": "Apache-2.0",
15
+ "author": "Cognite",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/cognitedata/dune.git",
19
+ "directory": "packages/dune"
20
+ },
21
+ "type": "module",
22
+ "main": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./src/index.ts",
27
+ "development": "./src/index.ts",
28
+ "default": "./dist/index.js"
29
+ },
30
+ "./auth": {
31
+ "types": "./src/auth/index.ts",
32
+ "development": "./src/auth/index.ts",
33
+ "default": "./dist/auth/index.js"
34
+ },
35
+ "./deploy": {
36
+ "types": "./src/deploy/index.ts",
37
+ "development": "./src/deploy/index.ts",
38
+ "default": "./dist/deploy/index.js"
39
+ },
40
+ "./vite": {
41
+ "types": "./src/vite/index.ts",
42
+ "development": "./src/vite/index.ts",
43
+ "default": "./dist/vite/index.js"
44
+ }
45
+ },
46
+ "bin": {
47
+ "dune": "./bin/cli.js"
48
+ },
49
+ "files": [
50
+ "bin",
51
+ "dist",
52
+ "_templates"
53
+ ],
54
+ "dependencies": {
55
+ "archiver": "^7.0.1",
56
+ "enquirer": "^2.4.1",
57
+ "execa": "^5.1.1",
58
+ "hygen": "^6.2.11"
59
+ },
60
+ "peerDependencies": {
61
+ "@cognite/sdk": ">=9.0.0",
62
+ "react": ">=18.0.0",
63
+ "react-dom": ">=18.0.0"
64
+ },
65
+ "peerDependenciesMeta": {
66
+ "@cognite/sdk": {
67
+ "optional": true
68
+ },
69
+ "react": {
70
+ "optional": true
71
+ },
72
+ "react-dom": {
73
+ "optional": true
74
+ }
75
+ },
76
+ "devDependencies": {
77
+ "@cognite/sdk": "^10.3.0",
78
+ "@types/archiver": "^7.0.0",
79
+ "@types/node": "^24.10.1",
80
+ "@types/react": "^19.2.6",
81
+ "@types/react-dom": "^19.2.3",
82
+ "react": "^19.2.0",
83
+ "react-dom": "^19.2.0",
84
+ "tsup": "^8.4.0",
85
+ "typescript": "^5.0.0"
86
+ },
87
+ "engines": {
88
+ "node": ">=18"
89
+ },
90
+ "scripts": {
91
+ "build": "tsup"
92
+ }
93
+ }