dataiku-sdk 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.
Files changed (48) hide show
  1. package/bin/dss.js +2 -0
  2. package/dist/packages/types/src/index.d.ts +458 -0
  3. package/dist/packages/types/src/index.js +384 -0
  4. package/dist/src/cli.d.ts +2 -0
  5. package/dist/src/cli.js +689 -0
  6. package/dist/src/client.d.ts +89 -0
  7. package/dist/src/client.js +301 -0
  8. package/dist/src/errors.d.ts +29 -0
  9. package/dist/src/errors.js +141 -0
  10. package/dist/src/index.d.ts +20 -0
  11. package/dist/src/index.js +24 -0
  12. package/dist/src/resources/base.d.ts +7 -0
  13. package/dist/src/resources/base.js +12 -0
  14. package/dist/src/resources/code-envs.d.ts +8 -0
  15. package/dist/src/resources/code-envs.js +36 -0
  16. package/dist/src/resources/connections.d.ts +20 -0
  17. package/dist/src/resources/connections.js +77 -0
  18. package/dist/src/resources/datasets.d.ts +57 -0
  19. package/dist/src/resources/datasets.js +423 -0
  20. package/dist/src/resources/folders.d.ts +15 -0
  21. package/dist/src/resources/folders.js +58 -0
  22. package/dist/src/resources/jobs.d.ts +72 -0
  23. package/dist/src/resources/jobs.js +184 -0
  24. package/dist/src/resources/notebooks.d.ts +34 -0
  25. package/dist/src/resources/notebooks.js +75 -0
  26. package/dist/src/resources/projects.d.ts +38 -0
  27. package/dist/src/resources/projects.js +185 -0
  28. package/dist/src/resources/recipes.d.ts +35 -0
  29. package/dist/src/resources/recipes.js +281 -0
  30. package/dist/src/resources/scenarios.d.ts +26 -0
  31. package/dist/src/resources/scenarios.js +57 -0
  32. package/dist/src/resources/sql.d.ts +40 -0
  33. package/dist/src/resources/sql.js +40 -0
  34. package/dist/src/resources/variables.d.ts +10 -0
  35. package/dist/src/resources/variables.js +22 -0
  36. package/dist/src/schemas.d.ts +7 -0
  37. package/dist/src/schemas.js +6 -0
  38. package/dist/src/utils/deep-merge.d.ts +1 -0
  39. package/dist/src/utils/deep-merge.js +15 -0
  40. package/dist/src/utils/flow-map.d.ts +37 -0
  41. package/dist/src/utils/flow-map.js +296 -0
  42. package/dist/src/utils/pagination.d.ts +8 -0
  43. package/dist/src/utils/pagination.js +23 -0
  44. package/dist/src/utils/sanitize.d.ts +2 -0
  45. package/dist/src/utils/sanitize.js +16 -0
  46. package/package.json +47 -0
  47. package/packages/types/dist/index.d.ts +458 -0
  48. package/packages/types/dist/index.js +384 -0
@@ -0,0 +1,89 @@
1
+ import { type Static, type TSchema } from "@sinclair/typebox";
2
+ import { CodeEnvsResource } from "./resources/code-envs.js";
3
+ import { ConnectionsResource } from "./resources/connections.js";
4
+ import { DatasetsResource } from "./resources/datasets.js";
5
+ import { FoldersResource } from "./resources/folders.js";
6
+ import { JobsResource } from "./resources/jobs.js";
7
+ import { NotebooksResource } from "./resources/notebooks.js";
8
+ import { ProjectsResource } from "./resources/projects.js";
9
+ import { RecipesResource } from "./resources/recipes.js";
10
+ import { ScenariosResource } from "./resources/scenarios.js";
11
+ import { SqlResource } from "./resources/sql.js";
12
+ import { VariablesResource } from "./resources/variables.js";
13
+ export interface DataikuClientConfig {
14
+ /** DSS base URL (e.g. https://dss.example.com) */
15
+ url: string;
16
+ /** API key for authentication */
17
+ apiKey: string;
18
+ /** Default project key — used when a resource method omits projectKey */
19
+ projectKey?: string;
20
+ /** Per-request timeout in milliseconds (default 30 000) */
21
+ requestTimeoutMs?: number;
22
+ /** Max retry attempts for idempotent requests (default 4, capped at 10) */
23
+ retryMaxAttempts?: number;
24
+ /**
25
+ * Called when an API response fails schema validation but data is still usable.
26
+ * Default: logs to console.warn. Set to a throwing function for strict mode.
27
+ * @param method - resource method that triggered the warning (e.g. "datasets.list")
28
+ * @param errors - human-readable validation error strings
29
+ */
30
+ onValidationWarning?: (method: string, errors: string[]) => void;
31
+ }
32
+ export declare class DataikuClient {
33
+ private readonly baseUrl;
34
+ private readonly apiKey;
35
+ private readonly defaultProjectKey;
36
+ private readonly requestTimeoutMs;
37
+ private readonly retryMaxAttempts;
38
+ private readonly onValidationWarning;
39
+ private _projects?;
40
+ private _datasets?;
41
+ private _recipes?;
42
+ private _jobs?;
43
+ private _scenarios?;
44
+ private _folders?;
45
+ private _variables?;
46
+ private _connections?;
47
+ private _codeEnvs?;
48
+ private _sql?;
49
+ private _notebooks?;
50
+ get projects(): ProjectsResource;
51
+ get datasets(): DatasetsResource;
52
+ get recipes(): RecipesResource;
53
+ get jobs(): JobsResource;
54
+ get scenarios(): ScenariosResource;
55
+ get folders(): FoldersResource;
56
+ get variables(): VariablesResource;
57
+ get connections(): ConnectionsResource;
58
+ get codeEnvs(): CodeEnvsResource;
59
+ get sql(): SqlResource;
60
+ get notebooks(): NotebooksResource;
61
+ constructor(config: DataikuClientConfig);
62
+ resolveProjectKey(paramValue?: string): string;
63
+ get<T = unknown>(path: string): Promise<T>;
64
+ getText(path: string): Promise<string>;
65
+ post<T = unknown>(path: string, body?: unknown): Promise<T>;
66
+ put<T = unknown>(path: string, body: unknown): Promise<T>;
67
+ del(path: string): Promise<void>;
68
+ putVoid(path: string, body: unknown): Promise<void>;
69
+ upload(path: string, filePath: string): Promise<void>;
70
+ stream(path: string): Promise<Response>;
71
+ private getHeaders;
72
+ private getAnyHeaders;
73
+ /**
74
+ * Validate raw data against a TypeBox schema, throwing on structural mismatch.
75
+ * Resources call this instead of bare `as T` casts for validated responses.
76
+ * Extra DSS fields (additionalProperties) are preserved in the returned data.
77
+ */
78
+ parse<S extends TSchema>(schema: S, data: unknown): Static<S>;
79
+ /**
80
+ * Validate raw data against a TypeBox schema without throwing.
81
+ * Always returns the data. On mismatch, fires onValidationWarning callback
82
+ * with the method name and error details.
83
+ */
84
+ safeParse<S extends TSchema>(schema: S, data: unknown, method: string): Static<S>;
85
+ /** Emit a validation warning via the configured callback. */
86
+ warn(method: string, errors: string[]): void;
87
+ private parseJsonResponse;
88
+ private fetchWithRetry;
89
+ }
@@ -0,0 +1,301 @@
1
+ import { Value, } from "@sinclair/typebox/value";
2
+ import { safeParseSchema, } from "./schemas.js";
3
+ import { classifyDataikuError, DataikuError, } from "./errors.js";
4
+ import { CodeEnvsResource, } from "./resources/code-envs.js";
5
+ import { ConnectionsResource, } from "./resources/connections.js";
6
+ import { DatasetsResource, } from "./resources/datasets.js";
7
+ import { FoldersResource, } from "./resources/folders.js";
8
+ import { JobsResource, } from "./resources/jobs.js";
9
+ import { NotebooksResource, } from "./resources/notebooks.js";
10
+ import { ProjectsResource, } from "./resources/projects.js";
11
+ import { RecipesResource, } from "./resources/recipes.js";
12
+ import { ScenariosResource, } from "./resources/scenarios.js";
13
+ import { SqlResource, } from "./resources/sql.js";
14
+ import { VariablesResource, } from "./resources/variables.js";
15
+ /* ------------------------------------------------------------------ */
16
+ /* Constants */
17
+ /* ------------------------------------------------------------------ */
18
+ const DEFAULT_RETRY_MAX_ATTEMPTS = 4;
19
+ const MAX_RETRY_ATTEMPTS_CAP = 10;
20
+ const BASE_DELAY_MS = 2_000;
21
+ const MAX_BACKOFF_DELAY_MS = 30_000;
22
+ const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
23
+ /* ------------------------------------------------------------------ */
24
+ /* Helpers */
25
+ /* ------------------------------------------------------------------ */
26
+ function defaultValidationWarning(method, errors) {
27
+ console.warn(`[dataiku-sdk] Schema validation warning in ${method}:\n ${errors.join("\n ")}`);
28
+ }
29
+ function sleep(ms) {
30
+ return new Promise((r) => setTimeout(r, ms));
31
+ }
32
+ function computeBackoffDelayMs(retryNumber) {
33
+ const cap = Math.min(MAX_BACKOFF_DELAY_MS, BASE_DELAY_MS * 2 ** Math.max(0, retryNumber - 1));
34
+ return Math.floor(Math.random() * (cap + 1));
35
+ }
36
+ function isTransientError(status, body) {
37
+ return classifyDataikuError(status, body).category === "transient";
38
+ }
39
+ function shouldRetryMethod(method) {
40
+ return method.toUpperCase() === "GET";
41
+ }
42
+ function buildRetryMetadata(method, enabled, maxAttempts, attempts, delaysMs, timedOut) {
43
+ return {
44
+ method,
45
+ enabled,
46
+ maxAttempts,
47
+ attempts,
48
+ retries: Math.max(0, attempts - 1),
49
+ delaysMs,
50
+ timedOut,
51
+ };
52
+ }
53
+ /* ------------------------------------------------------------------ */
54
+ /* Client */
55
+ /* ------------------------------------------------------------------ */
56
+ export class DataikuClient {
57
+ baseUrl;
58
+ apiKey;
59
+ defaultProjectKey;
60
+ requestTimeoutMs;
61
+ retryMaxAttempts;
62
+ onValidationWarning;
63
+ /* Resource namespaces — lazily initialized to break circular imports */
64
+ _projects;
65
+ _datasets;
66
+ _recipes;
67
+ _jobs;
68
+ _scenarios;
69
+ _folders;
70
+ _variables;
71
+ _connections;
72
+ _codeEnvs;
73
+ _sql;
74
+ _notebooks;
75
+ get projects() {
76
+ return (this._projects ??= new ProjectsResource(this));
77
+ }
78
+ get datasets() {
79
+ return (this._datasets ??= new DatasetsResource(this));
80
+ }
81
+ get recipes() {
82
+ return (this._recipes ??= new RecipesResource(this));
83
+ }
84
+ get jobs() {
85
+ return (this._jobs ??= new JobsResource(this));
86
+ }
87
+ get scenarios() {
88
+ return (this._scenarios ??= new ScenariosResource(this));
89
+ }
90
+ get folders() {
91
+ return (this._folders ??= new FoldersResource(this));
92
+ }
93
+ get variables() {
94
+ return (this._variables ??= new VariablesResource(this));
95
+ }
96
+ get connections() {
97
+ return (this._connections ??= new ConnectionsResource(this));
98
+ }
99
+ get codeEnvs() {
100
+ return (this._codeEnvs ??= new CodeEnvsResource(this));
101
+ }
102
+ get sql() {
103
+ return (this._sql ??= new SqlResource(this));
104
+ }
105
+ get notebooks() {
106
+ return (this._notebooks ??= new NotebooksResource(this));
107
+ }
108
+ constructor(config) {
109
+ const url = config.url?.trim();
110
+ if (!url)
111
+ throw new Error("DataikuClientConfig.url is required and must not be empty");
112
+ const apiKey = config.apiKey?.trim();
113
+ if (!apiKey)
114
+ throw new Error("DataikuClientConfig.apiKey is required and must not be empty");
115
+ this.baseUrl = url.replace(/\/+$/, "");
116
+ this.apiKey = apiKey;
117
+ this.defaultProjectKey = config.projectKey?.trim() || undefined;
118
+ this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
119
+ const rawMax = config.retryMaxAttempts ?? DEFAULT_RETRY_MAX_ATTEMPTS;
120
+ this.retryMaxAttempts = Math.min(Math.max(1, rawMax), MAX_RETRY_ATTEMPTS_CAP);
121
+ this.onValidationWarning = config.onValidationWarning ?? defaultValidationWarning;
122
+ }
123
+ /* ---- public: project key resolution ---- */
124
+ resolveProjectKey(paramValue) {
125
+ const pk = paramValue?.trim();
126
+ if (pk)
127
+ return pk;
128
+ if (this.defaultProjectKey)
129
+ return this.defaultProjectKey;
130
+ throw new Error("projectKey is required — pass it as a parameter or set projectKey in DataikuClientConfig");
131
+ }
132
+ /* ---- public: HTTP verbs ---- */
133
+ async get(path) {
134
+ const res = await this.fetchWithRetry(`${this.baseUrl}${path}`, {
135
+ method: "GET",
136
+ headers: this.getHeaders(),
137
+ });
138
+ return this.parseJsonResponse(res);
139
+ }
140
+ async getText(path) {
141
+ const res = await this.fetchWithRetry(`${this.baseUrl}${path}`, {
142
+ method: "GET",
143
+ headers: this.getAnyHeaders(),
144
+ });
145
+ return res.text();
146
+ }
147
+ async post(path, body) {
148
+ const res = await this.fetchWithRetry(`${this.baseUrl}${path}`, {
149
+ method: "POST",
150
+ headers: this.getHeaders(),
151
+ body: body !== undefined ? JSON.stringify(body) : undefined,
152
+ });
153
+ return this.parseJsonResponse(res);
154
+ }
155
+ async put(path, body) {
156
+ const res = await this.fetchWithRetry(`${this.baseUrl}${path}`, {
157
+ method: "PUT",
158
+ headers: this.getHeaders(),
159
+ body: JSON.stringify(body),
160
+ });
161
+ return this.parseJsonResponse(res);
162
+ }
163
+ async del(path) {
164
+ await this.fetchWithRetry(`${this.baseUrl}${path}`, {
165
+ method: "DELETE",
166
+ headers: this.getHeaders(),
167
+ });
168
+ }
169
+ async putVoid(path, body) {
170
+ await this.fetchWithRetry(`${this.baseUrl}${path}`, {
171
+ method: "PUT",
172
+ headers: this.getHeaders(),
173
+ body: JSON.stringify(body),
174
+ });
175
+ }
176
+ async upload(path, filePath) {
177
+ const { openAsBlob, } = await import("node:fs");
178
+ const { basename, } = await import("node:path");
179
+ const fileBlob = await openAsBlob(filePath);
180
+ const fileName = basename(filePath);
181
+ const formData = new FormData();
182
+ formData.append("file", fileBlob, fileName);
183
+ await this.fetchWithRetry(`${this.baseUrl}${path}`, {
184
+ method: "POST",
185
+ headers: { Authorization: `Bearer ${this.apiKey}`, },
186
+ body: formData,
187
+ });
188
+ }
189
+ async stream(path) {
190
+ return this.fetchWithRetry(`${this.baseUrl}${path}`, {
191
+ method: "GET",
192
+ headers: this.getAnyHeaders(),
193
+ });
194
+ }
195
+ /* ---- private: headers ---- */
196
+ getHeaders() {
197
+ return {
198
+ Authorization: `Bearer ${this.apiKey}`,
199
+ Accept: "application/json",
200
+ "Content-Type": "application/json",
201
+ };
202
+ }
203
+ getAnyHeaders() {
204
+ return {
205
+ Authorization: `Bearer ${this.apiKey}`,
206
+ Accept: "*/*",
207
+ };
208
+ }
209
+ /* ---- public: schema-validated parsing ---- */
210
+ /**
211
+ * Validate raw data against a TypeBox schema, throwing on structural mismatch.
212
+ * Resources call this instead of bare `as T` casts for validated responses.
213
+ * Extra DSS fields (additionalProperties) are preserved in the returned data.
214
+ */
215
+ parse(schema, data) {
216
+ Value.Assert(schema, data);
217
+ return data;
218
+ }
219
+ /**
220
+ * Validate raw data against a TypeBox schema without throwing.
221
+ * Always returns the data. On mismatch, fires onValidationWarning callback
222
+ * with the method name and error details.
223
+ */
224
+ safeParse(schema, data, method) {
225
+ const result = safeParseSchema(schema, data);
226
+ if (!result.success) {
227
+ this.onValidationWarning(method, result.errors);
228
+ }
229
+ return result.data;
230
+ }
231
+ /** Emit a validation warning via the configured callback. */
232
+ warn(method, errors) {
233
+ this.onValidationWarning(method, errors);
234
+ }
235
+ /* ---- private: JSON parsing ---- */
236
+ async parseJsonResponse(res) {
237
+ const text = await res.text();
238
+ if (!text)
239
+ return undefined;
240
+ try {
241
+ return JSON.parse(text);
242
+ }
243
+ catch {
244
+ const summary = text.length > 300 ? `${text.slice(0, 300)}…` : text;
245
+ throw new DataikuError(res.status, res.statusText || "Invalid JSON response", `Expected JSON response body but got non-JSON content: ${summary}`);
246
+ }
247
+ }
248
+ /* ---- private: retry loop ---- */
249
+ async fetchWithRetry(url, init) {
250
+ const method = (init.method ?? "GET").toUpperCase();
251
+ const retryEnabled = shouldRetryMethod(method);
252
+ const maxAttempts = retryEnabled ? this.retryMaxAttempts : 1;
253
+ const delaysMs = [];
254
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
255
+ let timedOut = false;
256
+ const controller = new AbortController();
257
+ const timeout = setTimeout(() => {
258
+ timedOut = true;
259
+ controller.abort();
260
+ }, this.requestTimeoutMs);
261
+ try {
262
+ const res = await fetch(url, { ...init, method, signal: controller.signal, });
263
+ if (!res.ok) {
264
+ const text = await res.text();
265
+ const canRetry = retryEnabled && attempt < maxAttempts && isTransientError(res.status, text);
266
+ if (canRetry) {
267
+ const delayMs = computeBackoffDelayMs(attempt);
268
+ delaysMs.push(delayMs);
269
+ await sleep(delayMs);
270
+ continue;
271
+ }
272
+ throw new DataikuError(res.status, res.statusText, text, buildRetryMetadata(method, retryEnabled, maxAttempts, attempt, delaysMs, false));
273
+ }
274
+ return res;
275
+ }
276
+ catch (error) {
277
+ if (error instanceof DataikuError)
278
+ throw error;
279
+ const canRetry = retryEnabled && attempt < maxAttempts;
280
+ if (canRetry) {
281
+ const delayMs = computeBackoffDelayMs(attempt);
282
+ delaysMs.push(delayMs);
283
+ await sleep(delayMs);
284
+ continue;
285
+ }
286
+ const detail = timedOut
287
+ ? `Request timed out after ${this.requestTimeoutMs}ms`
288
+ : error instanceof Error
289
+ ? error.message
290
+ : "Unknown transport error";
291
+ const statusText = timedOut ? "Request Timeout" : "Network Error";
292
+ throw new DataikuError(0, statusText, detail, buildRetryMetadata(method, retryEnabled, maxAttempts, attempt, delaysMs, timedOut));
293
+ }
294
+ finally {
295
+ clearTimeout(timeout);
296
+ }
297
+ }
298
+ // Unreachable in practice — the loop always throws or returns.
299
+ throw new DataikuError(0, "Network Error", "Request failed before receiving a response.", buildRetryMetadata(method, false, 1, 1, [], false));
300
+ }
301
+ }
@@ -0,0 +1,29 @@
1
+ export type DataikuErrorCategory = "not_found" | "forbidden" | "validation" | "transient" | "unknown";
2
+ export interface DataikuErrorTaxonomy {
3
+ category: DataikuErrorCategory;
4
+ retryable: boolean;
5
+ retryHint: string;
6
+ }
7
+ export interface DataikuRetryMetadata {
8
+ method: string;
9
+ enabled: boolean;
10
+ maxAttempts: number;
11
+ attempts: number;
12
+ retries: number;
13
+ delaysMs: number[];
14
+ timedOut: boolean;
15
+ }
16
+ export declare function classifyDataikuError(status: number, body: string): DataikuErrorTaxonomy;
17
+ export declare class DataikuError extends Error {
18
+ status: number;
19
+ statusText: string;
20
+ body: string;
21
+ category: DataikuErrorCategory;
22
+ retryable: boolean;
23
+ retryHint: string;
24
+ retry?: DataikuRetryMetadata;
25
+ constructor(status: number, statusText: string, body: string, retry?: DataikuRetryMetadata);
26
+ private static extractSummary;
27
+ private static formatRetryMetadata;
28
+ private static buildDetails;
29
+ }
@@ -0,0 +1,141 @@
1
+ export function classifyDataikuError(status, body) {
2
+ if (status === 0) {
3
+ return {
4
+ category: "transient",
5
+ retryable: true,
6
+ retryHint: "Network/transport failure. Retry with backoff and verify DSS URL reachability.",
7
+ };
8
+ }
9
+ const lowerBody = body.toLowerCase();
10
+ const isMissingDatasetRootPath = status === 500
11
+ && lowerBody.includes("root path of the dataset")
12
+ && lowerBody.includes("does not exist");
13
+ if (isMissingDatasetRootPath) {
14
+ return {
15
+ category: "validation",
16
+ retryable: false,
17
+ retryHint: "Dataset files are missing on storage. Build/materialize the dataset or upstream recipes before preview/download.",
18
+ };
19
+ }
20
+ const isServerNotFoundLike = status >= 500
21
+ && (lowerBody.includes("not found") || lowerBody.includes("does not exist"))
22
+ && ["dataset", "recipe", "scenario", "project", "folder",].some((token) => lowerBody.includes(token));
23
+ if (isServerNotFoundLike) {
24
+ return {
25
+ category: "not_found",
26
+ retryable: false,
27
+ retryHint: "Requested object was not found. Verify projectKey and object identifiers before retrying.",
28
+ };
29
+ }
30
+ const isServerValidationLike = status >= 500
31
+ && (lowerBody.includes("invalid")
32
+ || lowerBody.includes("validation")
33
+ || lowerBody.includes("bad request")
34
+ || lowerBody.includes("illegal argument"));
35
+ if (isServerValidationLike) {
36
+ return {
37
+ category: "validation",
38
+ retryable: false,
39
+ retryHint: "Request appears invalid for this endpoint. Fix parameters/payload before retrying.",
40
+ };
41
+ }
42
+ if (status === 404) {
43
+ const isHtmlGatewayResponse = lowerBody.includes("<!doctype html>");
44
+ return {
45
+ category: "not_found",
46
+ retryable: false,
47
+ retryHint: isHtmlGatewayResponse
48
+ ? "Resource was not found (gateway returned HTML). Verify DSS URL, projectKey, and object identifiers."
49
+ : "Verify projectKey and object identifiers (dataset/recipe/scenario/folder IDs).",
50
+ };
51
+ }
52
+ if (status === 401 || status === 403) {
53
+ return {
54
+ category: "forbidden",
55
+ retryable: false,
56
+ retryHint: "Check API key validity and project permissions for the requested action.",
57
+ };
58
+ }
59
+ if (status === 400 || status === 409 || status === 422) {
60
+ return {
61
+ category: "validation",
62
+ retryable: false,
63
+ retryHint: "Fix request parameters/payload and try again (same request will likely fail).",
64
+ };
65
+ }
66
+ if (status === 408 || status === 425 || status === 429 || (status >= 500 && status <= 599)) {
67
+ return {
68
+ category: "transient",
69
+ retryable: true,
70
+ retryHint: "Retry with exponential backoff. If it persists, check DSS availability and upstream proxies.",
71
+ };
72
+ }
73
+ return {
74
+ category: "unknown",
75
+ retryable: false,
76
+ retryHint: "Inspect the response details and DSS logs to determine whether retry is appropriate.",
77
+ };
78
+ }
79
+ export class DataikuError extends Error {
80
+ status;
81
+ statusText;
82
+ body;
83
+ category;
84
+ retryable;
85
+ retryHint;
86
+ retry;
87
+ constructor(status, statusText, body, retry) {
88
+ const details = DataikuError.buildDetails(status, statusText, body, retry);
89
+ super(details.message);
90
+ this.status = status;
91
+ this.statusText = statusText;
92
+ this.body = body;
93
+ this.name = "DataikuError";
94
+ this.category = details.category;
95
+ this.retryable = details.retryable;
96
+ this.retryHint = details.retryHint;
97
+ this.retry = retry;
98
+ }
99
+ static extractSummary(_status, _statusText, body) {
100
+ try {
101
+ const parsed = JSON.parse(body);
102
+ if (parsed.message)
103
+ return String(parsed.message);
104
+ }
105
+ catch {
106
+ // not JSON — use raw body
107
+ }
108
+ if (!body)
109
+ return "(empty response body)";
110
+ return body.length > 200 ? `${body.slice(0, 200)}…` : body;
111
+ }
112
+ static formatRetryMetadata(retry) {
113
+ if (!retry)
114
+ return undefined;
115
+ const shownDelays = retry.delaysMs.slice(0, 10);
116
+ const delaysSuffix = retry.delaysMs.length > shownDelays.length ? ", …" : "";
117
+ const delaysPart = shownDelays.length > 0 ? `[${shownDelays.join(", ")}${delaysSuffix}]` : "[]";
118
+ return [
119
+ `Retry attempts: ${retry.attempts}/${retry.maxAttempts}`,
120
+ `Retry policy: ${retry.enabled ? "enabled" : "disabled"} for ${retry.method}`,
121
+ `Retries performed: ${retry.retries}`,
122
+ `Backoff delays (ms): ${delaysPart}`,
123
+ `Timed out: ${retry.timedOut ? "yes" : "no"}`,
124
+ ].join(" | ");
125
+ }
126
+ static buildDetails(status, statusText, body, retry) {
127
+ const summary = DataikuError.extractSummary(status, statusText, body);
128
+ const taxonomy = classifyDataikuError(status, body);
129
+ const retrySummary = DataikuError.formatRetryMetadata(retry);
130
+ return {
131
+ ...taxonomy,
132
+ message: [
133
+ `${status} ${statusText}: ${summary}`,
134
+ `Error type: ${taxonomy.category}`,
135
+ `Retryable: ${taxonomy.retryable ? "yes" : "no"}`,
136
+ `Hint: ${taxonomy.retryHint}`,
137
+ ...(retrySummary ? [retrySummary,] : []),
138
+ ].join("\n"),
139
+ };
140
+ }
141
+ }
@@ -0,0 +1,20 @@
1
+ export { DataikuClient, type DataikuClientConfig, } from "./client.js";
2
+ export { DataikuError, type DataikuErrorCategory, type DataikuErrorTaxonomy, type DataikuRetryMetadata, } from "./errors.js";
3
+ export { CodeEnvsResource, } from "./resources/code-envs.js";
4
+ export { ConnectionsResource, } from "./resources/connections.js";
5
+ export { DatasetsResource, } from "./resources/datasets.js";
6
+ export { FoldersResource, } from "./resources/folders.js";
7
+ export { computeNextPollDelayMs, JobsResource, } from "./resources/jobs.js";
8
+ export { NotebooksResource, } from "./resources/notebooks.js";
9
+ export { type FlowMapResult, ProjectsResource, } from "./resources/projects.js";
10
+ export { RecipesResource, } from "./resources/recipes.js";
11
+ export { ScenariosResource, } from "./resources/scenarios.js";
12
+ export { SqlResource, } from "./resources/sql.js";
13
+ export { VariablesResource, } from "./resources/variables.js";
14
+ export { BuildModeSchema, CodeEnvDetailsSchema, CodeEnvSummaryArraySchema, CodeEnvSummarySchema, ConnectionSummarySchema, DatasetCreateOptionsSchema, DatasetDetailsSchema, DatasetSchemaSchema, DatasetSummaryArraySchema, DatasetSummarySchema, FlowMapOptionsSchema, FolderDetailsSchema, FolderItemArraySchema, FolderItemSchema, FolderSummaryArraySchema, FolderSummarySchema, JobSummaryArraySchema, JobSummarySchema, JobWaitResultSchema, JupyterCellSchema, JupyterNotebookContentSchema, JupyterNotebookSummaryArraySchema, JupyterNotebookSummarySchema, NotebookSessionArraySchema, NotebookSessionSchema, parseSchema, ProjectDetailsSchema, ProjectMetadataSchema, ProjectSummaryArraySchema, ProjectSummarySchema, ProjectVariablesSchema, RecipeCreateOptionsSchema, RecipeCreateResultSchema, RecipeDetailsSchema, RecipeSummaryArraySchema, RecipeSummarySchema, safeParseSchema, ScenarioDetailsSchema, ScenarioStatusSchema, ScenarioSummaryArraySchema, ScenarioSummarySchema, SqlNotebookCellSchema, SqlNotebookContentSchema, SqlNotebookSummaryArraySchema, SqlNotebookSummarySchema, SqlQueryResponseSchema, SqlQueryResultSchema, SqlQuerySchemaSchema, } from "./schemas.js";
15
+ export type { SafeParseResult, } from "./schemas.js";
16
+ export type { BuildMode, CodeEnvDetails, CodeEnvSummary, ConnectionSummary, DatasetCreateOptions, DatasetDetails, DatasetSchema, DatasetSummary, FlowMapOptions, FolderDetails, FolderItem, FolderSummary, JobSummary, JobWaitResult, JupyterCell, JupyterNotebookContent, JupyterNotebookSummary, NotebookSession, ProjectDetails, ProjectMetadata, ProjectSummary, ProjectVariables, RecipeCreateOptions, RecipeCreateResult, RecipeDetails, RecipeSummary, ScenarioDetails, ScenarioStatus, ScenarioSummary, SqlNotebookCell, SqlNotebookContent, SqlNotebookSummary, SqlQueryResponse, SqlQueryResult, SqlQuerySchema, } from "./schemas.js";
17
+ export { deepMerge, } from "./utils/deep-merge.js";
18
+ export { type NormalizedFlowEdge, type NormalizedFlowMap, type NormalizedFlowNode, normalizeFlowGraph, } from "./utils/flow-map.js";
19
+ export { sanitizeFileName, } from "./utils/sanitize.js";
20
+ export { validateStreamColumns, } from "./resources/datasets.js";
@@ -0,0 +1,24 @@
1
+ // Client
2
+ export { DataikuClient, } from "./client.js";
3
+ // Errors
4
+ export { DataikuError, } from "./errors.js";
5
+ // Resources (for advanced use / extension)
6
+ export { CodeEnvsResource, } from "./resources/code-envs.js";
7
+ export { ConnectionsResource, } from "./resources/connections.js";
8
+ export { DatasetsResource, } from "./resources/datasets.js";
9
+ export { FoldersResource, } from "./resources/folders.js";
10
+ export { computeNextPollDelayMs, JobsResource, } from "./resources/jobs.js";
11
+ export { NotebooksResource, } from "./resources/notebooks.js";
12
+ export { ProjectsResource, } from "./resources/projects.js";
13
+ export { RecipesResource, } from "./resources/recipes.js";
14
+ export { ScenariosResource, } from "./resources/scenarios.js";
15
+ export { SqlResource, } from "./resources/sql.js";
16
+ export { VariablesResource, } from "./resources/variables.js";
17
+ // Schemas (TypeBox schema objects for runtime validation)
18
+ export { BuildModeSchema, CodeEnvDetailsSchema, CodeEnvSummaryArraySchema, CodeEnvSummarySchema, ConnectionSummarySchema, DatasetCreateOptionsSchema, DatasetDetailsSchema, DatasetSchemaSchema, DatasetSummaryArraySchema, DatasetSummarySchema, FlowMapOptionsSchema, FolderDetailsSchema, FolderItemArraySchema, FolderItemSchema, FolderSummaryArraySchema, FolderSummarySchema, JobSummaryArraySchema, JobSummarySchema, JobWaitResultSchema, JupyterCellSchema, JupyterNotebookContentSchema, JupyterNotebookSummaryArraySchema, JupyterNotebookSummarySchema, NotebookSessionArraySchema, NotebookSessionSchema, parseSchema, ProjectDetailsSchema, ProjectMetadataSchema, ProjectSummaryArraySchema, ProjectSummarySchema, ProjectVariablesSchema, RecipeCreateOptionsSchema, RecipeCreateResultSchema, RecipeDetailsSchema, RecipeSummaryArraySchema, RecipeSummarySchema, safeParseSchema, ScenarioDetailsSchema, ScenarioStatusSchema, ScenarioSummaryArraySchema, ScenarioSummarySchema, SqlNotebookCellSchema, SqlNotebookContentSchema, SqlNotebookSummaryArraySchema, SqlNotebookSummarySchema, SqlQueryResponseSchema, SqlQueryResultSchema, SqlQuerySchemaSchema, } from "./schemas.js";
19
+ // Utilities
20
+ export { deepMerge, } from "./utils/deep-merge.js";
21
+ export { normalizeFlowGraph, } from "./utils/flow-map.js";
22
+ export { sanitizeFileName, } from "./utils/sanitize.js";
23
+ // Stream validation
24
+ export { validateStreamColumns, } from "./resources/datasets.js";
@@ -0,0 +1,7 @@
1
+ import type { DataikuClient } from "../client.js";
2
+ export declare abstract class BaseResource {
3
+ protected readonly client: DataikuClient;
4
+ constructor(client: DataikuClient);
5
+ protected resolveProjectKey(pk?: string): string;
6
+ protected enc(pk?: string): string;
7
+ }
@@ -0,0 +1,12 @@
1
+ export class BaseResource {
2
+ client;
3
+ constructor(client) {
4
+ this.client = client;
5
+ }
6
+ resolveProjectKey(pk) {
7
+ return this.client.resolveProjectKey(pk);
8
+ }
9
+ enc(pk) {
10
+ return encodeURIComponent(this.resolveProjectKey(pk));
11
+ }
12
+ }
@@ -0,0 +1,8 @@
1
+ import type { CodeEnvDetails, CodeEnvSummary } from "../schemas.js";
2
+ import { BaseResource } from "./base.js";
3
+ export declare class CodeEnvsResource extends BaseResource {
4
+ list(opts?: {
5
+ envLang?: "PYTHON" | "R";
6
+ }): Promise<CodeEnvSummary[]>;
7
+ get(envLang: string, envName: string): Promise<CodeEnvDetails>;
8
+ }
@@ -0,0 +1,36 @@
1
+ import { CodeEnvSummaryArraySchema, } from "../schemas.js";
2
+ import { BaseResource, } from "./base.js";
3
+ export class CodeEnvsResource extends BaseResource {
4
+ async list(opts) {
5
+ const raw = await this.client.get("/public/api/admin/code-envs/");
6
+ const envs = this.client.safeParse(CodeEnvSummaryArraySchema, raw, "codeEnvs.list");
7
+ if (opts?.envLang) {
8
+ return envs.filter((e) => e.envLang === opts.envLang);
9
+ }
10
+ return envs;
11
+ }
12
+ async get(envLang, envName) {
13
+ const langEnc = encodeURIComponent(envLang);
14
+ const nameEnc = encodeURIComponent(envName);
15
+ const raw = await this.client.get(`/public/api/admin/code-envs/${langEnc}/${nameEnc}/`);
16
+ const desc = (raw.desc ?? {});
17
+ const requestedPackages = splitPackageList(raw.specPackageList);
18
+ requestedPackages.sort();
19
+ const installedPackages = splitPackageList(raw.actualPackageList);
20
+ return {
21
+ envName: raw.envName ?? envName,
22
+ envLang: raw.envLang ?? envLang,
23
+ pythonInterpreter: desc.pythonInterpreter,
24
+ requestedPackages,
25
+ installedPackages,
26
+ };
27
+ }
28
+ }
29
+ function splitPackageList(raw) {
30
+ if (!raw)
31
+ return [];
32
+ return raw
33
+ .split("\n")
34
+ .map((line) => line.trim())
35
+ .filter((line) => line.length > 0);
36
+ }