@deepnote/blocks 1.3.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/index.js ADDED
@@ -0,0 +1,568 @@
1
+ import { z } from "zod";
2
+ import { parse } from "yaml";
3
+ import { dedent } from "ts-dedent";
4
+
5
+ //#region src/deserialize-file/deepnote-file-schema.ts
6
+ const deepnoteBlockSchema = z.object({
7
+ blockGroup: z.string().optional(),
8
+ content: z.string().optional(),
9
+ executionCount: z.number().optional(),
10
+ id: z.string(),
11
+ metadata: z.record(z.any()).optional(),
12
+ outputs: z.array(z.any()).optional(),
13
+ sortingKey: z.string(),
14
+ type: z.string(),
15
+ version: z.number().optional()
16
+ });
17
+ const deepnoteFileSchema = z.object({
18
+ metadata: z.object({
19
+ checksum: z.string().optional(),
20
+ createdAt: z.string(),
21
+ exportedAt: z.string().optional(),
22
+ modifiedAt: z.string().optional()
23
+ }),
24
+ project: z.object({
25
+ id: z.string(),
26
+ initNotebookId: z.string().optional(),
27
+ integrations: z.array(z.object({
28
+ id: z.string(),
29
+ name: z.string(),
30
+ type: z.string()
31
+ })).optional(),
32
+ name: z.string(),
33
+ notebooks: z.array(z.object({
34
+ blocks: z.array(deepnoteBlockSchema),
35
+ executionMode: z.enum(["block", "downstream"]).optional(),
36
+ id: z.string(),
37
+ isModule: z.boolean().optional(),
38
+ name: z.string(),
39
+ workingDirectory: z.string().optional()
40
+ })),
41
+ settings: z.object({
42
+ environment: z.object({
43
+ customImage: z.string().optional(),
44
+ pythonVersion: z.string().optional()
45
+ }).optional(),
46
+ requirements: z.array(z.string()).optional(),
47
+ sqlCacheMaxAge: z.number().optional()
48
+ }).optional()
49
+ }),
50
+ version: z.string()
51
+ });
52
+
53
+ //#endregion
54
+ //#region src/deserialize-file/parse-yaml.ts
55
+ function parseYaml(yamlContent) {
56
+ try {
57
+ return parse(yamlContent);
58
+ } catch (error) {
59
+ const message = error instanceof Error ? error.message : String(error);
60
+ throw new Error(`Failed to parse Deepnote file: ${message}`);
61
+ }
62
+ }
63
+
64
+ //#endregion
65
+ //#region src/deserialize-file/deserialize-deepnote-file.ts
66
+ /**
67
+ * Deserialize a YAML string into a DeepnoteFile object.
68
+ */
69
+ function deserializeDeepnoteFile(yamlContent) {
70
+ const parsed = parseYaml(yamlContent);
71
+ const result = deepnoteFileSchema.safeParse(parsed);
72
+ if (!result.success) {
73
+ const issue = result.error.issues[0];
74
+ if (!issue) throw new Error("Invalid Deepnote file.");
75
+ const path = issue.path.join(".");
76
+ const message = path ? `${path}: ${issue.message}` : issue.message;
77
+ throw new Error(`Failed to parse the Deepnote file: ${message}.`);
78
+ }
79
+ return result.data;
80
+ }
81
+
82
+ //#endregion
83
+ //#region src/blocks.ts
84
+ var UnsupportedBlockTypeError = class extends Error {
85
+ constructor(message) {
86
+ super(message);
87
+ this.name = "UnsupportedBlockTypeError";
88
+ }
89
+ };
90
+
91
+ //#endregion
92
+ //#region src/blocks/image-blocks.ts
93
+ function escapeHtmlAttribute(value) {
94
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
95
+ }
96
+ function sanitizeWidth(width) {
97
+ return width.replace(/[^0-9]/g, "") || "";
98
+ }
99
+ function sanitizeAlignment(alignment) {
100
+ return [
101
+ "left",
102
+ "center",
103
+ "right"
104
+ ].includes(alignment.toLowerCase()) ? alignment.toLowerCase() : "";
105
+ }
106
+ function createMarkdownForImageBlock(block) {
107
+ return `<img src="${escapeHtmlAttribute(block.metadata.deepnote_img_src ?? "")}" width="${sanitizeWidth(block.metadata.deepnote_img_width ?? "")}" align="${sanitizeAlignment(block.metadata.deepnote_img_alignment ?? "")}" />`;
108
+ }
109
+ function isImageBlock(block) {
110
+ return block.type === "image";
111
+ }
112
+
113
+ //#endregion
114
+ //#region src/blocks/text-blocks.ts
115
+ function isTextBlock(block) {
116
+ return [
117
+ "text-cell-p",
118
+ "text-cell-h1",
119
+ "text-cell-h2",
120
+ "text-cell-h3",
121
+ "text-cell-bullet",
122
+ "text-cell-todo",
123
+ "text-cell-callout"
124
+ ].includes(block.type.toLowerCase());
125
+ }
126
+ function escapeMarkdown(text) {
127
+ return text.replace(/([\\`*_{}[\]()#+\-.!|>])/g, "\\$1");
128
+ }
129
+ function createMarkdownForTextBlock(block) {
130
+ if (block.type === "text-cell-h1") return `# ${escapeMarkdown(block.content)}`;
131
+ if (block.type === "text-cell-h2") return `## ${escapeMarkdown(block.content)}`;
132
+ if (block.type === "text-cell-h3") return `### ${escapeMarkdown(block.content)}`;
133
+ if (block.type === "text-cell-bullet") return `- ${escapeMarkdown(block.content)}`;
134
+ if (block.type === "text-cell-todo") return `- ${block.metadata.checked ? "[x]" : "[ ]"} ${escapeMarkdown(block.content)}`;
135
+ if (block.type === "text-cell-callout") return `> ${escapeMarkdown(block.content)}`;
136
+ if (block.type === "text-cell-p") return escapeMarkdown(block.content);
137
+ throw new Error("Unhandled block type.");
138
+ }
139
+ function stripMarkdownFromTextBlock(block) {
140
+ if (block.type === "text-cell-h1") return block.content.replace(/^#\s+/, "").trim();
141
+ if (block.type === "text-cell-h2") return block.content.replace(/^##\s+/, "").trim();
142
+ if (block.type === "text-cell-h3") return block.content.replace(/^#{3,6}\s+/, "").trim();
143
+ if (block.type === "text-cell-bullet") return block.content.replace(/^-+\s+/, "").trim();
144
+ if (block.type === "text-cell-todo") return block.content.replace(/^-+\s+\[.\]\s+/, "").trim();
145
+ if (block.type === "text-cell-callout") return block.content.replace(/^>\s+/, "").trim();
146
+ if (block.type === "text-cell-p") return block.content.trim();
147
+ throw new Error("Unhandled block type.");
148
+ }
149
+ function createMarkdownForSeparatorBlock(_block) {
150
+ return "<hr>";
151
+ }
152
+ function isSeparatorBlock(block) {
153
+ return block.type === "separator";
154
+ }
155
+
156
+ //#endregion
157
+ //#region src/markdown.ts
158
+ function createMarkdown(block) {
159
+ if (block.type === "markdown") return block.content ?? "";
160
+ if (isTextBlock(block)) return createMarkdownForTextBlock(block);
161
+ if (isSeparatorBlock(block)) return createMarkdownForSeparatorBlock(block);
162
+ if (isImageBlock(block)) return createMarkdownForImageBlock(block);
163
+ throw new UnsupportedBlockTypeError(`Creating markdown from block type ${block.type} is not supported yet.`);
164
+ }
165
+ function stripMarkdown(block) {
166
+ if (isTextBlock(block)) return stripMarkdownFromTextBlock(block);
167
+ throw new UnsupportedBlockTypeError(`Stripping markdown from block type ${block.type} is not supported yet.`);
168
+ }
169
+
170
+ //#endregion
171
+ //#region src/blocks/python-utils.ts
172
+ function escapePythonString(value) {
173
+ return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "\\'").replaceAll("\n", "\\n")}'`;
174
+ }
175
+ function sanitizePythonVariableName(name, options = {}) {
176
+ let sanitizedVariableName = name.replace(/\s+/g, "_").replace(/[^0-9a-zA-Z_]/g, "").replace(/^[^a-zA-Z_]+/g, "");
177
+ if (sanitizedVariableName === "" && !options.disableEmptyFallback) sanitizedVariableName = "input_1";
178
+ return sanitizedVariableName;
179
+ }
180
+
181
+ //#endregion
182
+ //#region src/python-snippets.ts
183
+ const pythonCode = {
184
+ setVariableContextValue: (variableName, value) => {
185
+ return `${sanitizePythonVariableName(variableName)} = ${value ? "True" : "False"}`;
186
+ },
187
+ executeBigNumber: (titleTemplate, valueVariableName, comparisonTitleTemplate = "", comparisonVariableName = "") => {
188
+ const sanitizedValueVariable = sanitizePythonVariableName(valueVariableName);
189
+ const valuePart = sanitizedValueVariable ? `f"{${sanitizedValueVariable}}"` : `""`;
190
+ if (!(comparisonTitleTemplate || comparisonVariableName)) return `
191
+ def __deepnote_big_number__():
192
+ import json
193
+ import jinja2
194
+ from jinja2 import meta
195
+
196
+ def render_template(template):
197
+ parsed_content = jinja2.Environment().parse(template)
198
+
199
+ required_variables = meta.find_undeclared_variables(parsed_content)
200
+
201
+ context = {
202
+ variable_name: globals().get(variable_name)
203
+ for variable_name in required_variables
204
+ }
205
+
206
+ result = jinja2.Environment().from_string(template).render(context)
207
+
208
+ return result
209
+
210
+ rendered_title = render_template(${escapePythonString(titleTemplate)})
211
+
212
+ return json.dumps({
213
+ "title": rendered_title,
214
+ "value": ${valuePart}
215
+ })
216
+
217
+ __deepnote_big_number__()
218
+ `;
219
+ const sanitizedComparisonVariable = sanitizePythonVariableName(comparisonVariableName);
220
+ const comparisonValuePart = sanitizedComparisonVariable ? `f"{${sanitizedComparisonVariable}}"` : `""`;
221
+ return `
222
+ def __deepnote_big_number__():
223
+ import json
224
+ import jinja2
225
+ from jinja2 import meta
226
+
227
+ def render_template(template):
228
+ parsed_content = jinja2.Environment().parse(template)
229
+
230
+ required_variables = meta.find_undeclared_variables(parsed_content)
231
+
232
+ context = {
233
+ variable_name: globals().get(variable_name)
234
+ for variable_name in required_variables
235
+ }
236
+
237
+ result = jinja2.Environment().from_string(template).render(context)
238
+
239
+ return result
240
+
241
+ rendered_title = render_template(${escapePythonString(titleTemplate)})
242
+ rendered_comparison_title = render_template(${escapePythonString(comparisonTitleTemplate)})
243
+
244
+ return json.dumps({
245
+ "comparisonTitle": rendered_comparison_title,
246
+ "comparisonValue": ${comparisonValuePart},
247
+ "title": rendered_title,
248
+ "value": ${valuePart}
249
+ })
250
+
251
+ __deepnote_big_number__()
252
+ `;
253
+ },
254
+ executeVisualization: (variableName, spec, filters) => {
255
+ return `_dntk.DeepnoteChart(${sanitizePythonVariableName(variableName)}, ${escapePythonString(spec)}, filters=${escapePythonString(filters)})`;
256
+ },
257
+ dateRangePast7days: (name) => {
258
+ return dedent`
259
+ from datetime import datetime as _deepnote_datetime, timedelta as _deepnote_timedelta
260
+ ${sanitizePythonVariableName(name)} = [_deepnote_datetime.now().date() - _deepnote_timedelta(days=7), _deepnote_datetime.now().date()]
261
+ `;
262
+ },
263
+ dateRangePast14days: (name) => {
264
+ return dedent`
265
+ from datetime import datetime as _deepnote_datetime, timedelta as _deepnote_timedelta
266
+ ${sanitizePythonVariableName(name)} = [_deepnote_datetime.now().date() - _deepnote_timedelta(days=14), _deepnote_datetime.now().date()]
267
+ `;
268
+ },
269
+ dateRangePastMonth: (name) => {
270
+ return dedent`
271
+ from datetime import datetime as _deepnote_datetime
272
+ from dateutil.relativedelta import relativedelta
273
+ ${sanitizePythonVariableName(name)} = [_deepnote_datetime.now().date() - relativedelta(months=1), _deepnote_datetime.now().date()]
274
+ `;
275
+ },
276
+ dateRangePast3months: (name) => {
277
+ return dedent`
278
+ from datetime import datetime as _deepnote_datetime
279
+ from dateutil.relativedelta import relativedelta
280
+ ${sanitizePythonVariableName(name)} = [_deepnote_datetime.now().date() - relativedelta(months=3), _deepnote_datetime.now().date()]
281
+ `;
282
+ },
283
+ dateRangePast6months: (name) => {
284
+ return dedent`
285
+ from datetime import datetime as _deepnote_datetime
286
+ from dateutil.relativedelta import relativedelta
287
+ ${sanitizePythonVariableName(name)} = [_deepnote_datetime.now().date() - relativedelta(months=6), _deepnote_datetime.now().date()]
288
+ `;
289
+ },
290
+ dateRangePastYear: (name) => {
291
+ return dedent`
292
+ from datetime import datetime as _deepnote_datetime
293
+ from dateutil.relativedelta import relativedelta
294
+ ${sanitizePythonVariableName(name)} = [_deepnote_datetime.now().date() - relativedelta(years=1), _deepnote_datetime.now().date()]
295
+ `;
296
+ },
297
+ dateRangeCustomDays: (name, days) => {
298
+ return dedent`
299
+ from datetime import datetime, timedelta
300
+ ${sanitizePythonVariableName(name)} = [datetime.now().date() - timedelta(days=${Math.floor(Number(days)) || 0}), datetime.now().date()]
301
+ `;
302
+ },
303
+ dateRangeAbsolute: (name, startDate, endDate) => {
304
+ const sanitizedName = sanitizePythonVariableName(name);
305
+ const escapedStartDate = escapePythonString(startDate);
306
+ const escapedEndDate = escapePythonString(endDate);
307
+ return dedent`
308
+ from dateutil.parser import parse as _deepnote_parse
309
+ ${sanitizedName} = [${startDate ? `_deepnote_parse(${escapedStartDate}).date()` : "None"}, ${endDate ? `_deepnote_parse(${escapedEndDate}).date()` : "None"}]
310
+ `;
311
+ }
312
+ };
313
+
314
+ //#endregion
315
+ //#region src/blocks/big-number-blocks.ts
316
+ function createPythonCodeForBigNumberBlock(block) {
317
+ return pythonCode.executeBigNumber(block.metadata.deepnote_big_number_title ?? "", block.metadata.deepnote_big_number_value ?? "", block.metadata.deepnote_big_number_comparison_title, block.metadata.deepnote_big_number_comparison_value);
318
+ }
319
+ function isBigNumberBlock(block) {
320
+ return block.type === "big-number";
321
+ }
322
+
323
+ //#endregion
324
+ //#region src/blocks/button-blocks.ts
325
+ function createPythonCodeForButtonBlock(block, executionContext) {
326
+ if (block.metadata.deepnote_button_behavior === "set_variable" && block.metadata.deepnote_variable_name) {
327
+ const sanitizedPythonVariableName = sanitizePythonVariableName(block.metadata.deepnote_variable_name);
328
+ if (executionContext?.variableContext?.includes(sanitizedPythonVariableName)) return pythonCode.setVariableContextValue(sanitizedPythonVariableName, true);
329
+ return pythonCode.setVariableContextValue(sanitizedPythonVariableName, false);
330
+ }
331
+ return "";
332
+ }
333
+ function isButtonBlock(block) {
334
+ return block.type === "button";
335
+ }
336
+
337
+ //#endregion
338
+ //#region src/blocks/data-frame.ts
339
+ function createDataFrameConfig(block) {
340
+ const tableState = block.metadata.deepnote_table_state ?? {};
341
+ const tableStateAsJson = JSON.stringify(tableState);
342
+ return dedent`
343
+ if '_dntk' in globals():
344
+ _dntk.dataframe_utils.configure_dataframe_formatter(${escapePythonString(tableStateAsJson)})
345
+ else:
346
+ _deepnote_current_table_attrs = ${escapePythonString(tableStateAsJson)}
347
+ `;
348
+ }
349
+
350
+ //#endregion
351
+ //#region src/blocks/code-blocks.ts
352
+ function createPythonCodeForCodeBlock(block) {
353
+ return dedent`
354
+ ${createDataFrameConfig(block)}
355
+
356
+ ${block.content}
357
+ `;
358
+ }
359
+ function isCodeBlock(block) {
360
+ return block.type === "code";
361
+ }
362
+
363
+ //#endregion
364
+ //#region src/blocks/input-blocks.ts
365
+ const DATE_RANGE_INPUT_RELATIVE_RANGES = [
366
+ {
367
+ value: "past7days",
368
+ pythonCode: pythonCode.dateRangePast7days
369
+ },
370
+ {
371
+ value: "past14days",
372
+ pythonCode: pythonCode.dateRangePast14days
373
+ },
374
+ {
375
+ value: "pastMonth",
376
+ pythonCode: pythonCode.dateRangePastMonth
377
+ },
378
+ {
379
+ value: "past3months",
380
+ pythonCode: pythonCode.dateRangePast3months
381
+ },
382
+ {
383
+ value: "past6months",
384
+ pythonCode: pythonCode.dateRangePast6months
385
+ },
386
+ {
387
+ value: "pastYear",
388
+ pythonCode: pythonCode.dateRangePastYear
389
+ }
390
+ ];
391
+ function isCustomDateRange(value) {
392
+ if (typeof value !== "string") return false;
393
+ const days = Number.parseInt(value.split("customDays")[1] ?? "0", 10);
394
+ return value.startsWith("customDays") && !Number.isNaN(days) && days >= 0;
395
+ }
396
+ function isValidRelativeDateInterval(value) {
397
+ return typeof value === "string" && DATE_RANGE_INPUT_RELATIVE_RANGES.some((range) => range.value === value);
398
+ }
399
+ function isValidAbsoluteDateRange(value) {
400
+ const YYYY_MM_DD_REGEX = /^\d{4}-\d{2}-\d{2}$/;
401
+ return Array.isArray(value) && value.length === 2 && value.every((v) => typeof v === "string" && (v === "" || YYYY_MM_DD_REGEX.test(v)));
402
+ }
403
+ function createPythonCodeForInputTextBlock(block) {
404
+ return `${sanitizePythonVariableName(block.metadata.deepnote_variable_name)} = ${escapePythonString(block.metadata.deepnote_variable_value)}`;
405
+ }
406
+ function createPythonCodeForInputTextareaBlock(block) {
407
+ return `${sanitizePythonVariableName(block.metadata.deepnote_variable_name)} = ${escapePythonString(block.metadata.deepnote_variable_value)}`;
408
+ }
409
+ function createPythonCodeForInputCheckboxBlock(block) {
410
+ return `${sanitizePythonVariableName(block.metadata.deepnote_variable_name)} = ${block.metadata.deepnote_variable_value ? "True" : "False"}`;
411
+ }
412
+ function createPythonCodeForInputSelectBlock(block) {
413
+ const sanitizedPythonVariableName = sanitizePythonVariableName(block.metadata.deepnote_variable_name);
414
+ if (block.metadata.deepnote_allow_multiple_values || Array.isArray(block.metadata.deepnote_variable_value)) return `${sanitizedPythonVariableName} = [${(Array.isArray(block.metadata.deepnote_variable_value) ? block.metadata.deepnote_variable_value : [block.metadata.deepnote_variable_value]).map((value) => escapePythonString(value)).join(", ")}]`;
415
+ else if (!block.metadata.deepnote_allow_multiple_values && !block.metadata.deepnote_variable_value) return `${sanitizedPythonVariableName} = None`;
416
+ else return `${sanitizedPythonVariableName} = ${escapePythonString(block.metadata.deepnote_variable_value)}`;
417
+ }
418
+ function createPythonCodeForInputSliderBlock(block) {
419
+ const sanitizedPythonVariableName = sanitizePythonVariableName(block.metadata.deepnote_variable_name);
420
+ const value = block.metadata.deepnote_variable_value;
421
+ if (!/^-?\d+\.?\d*$|^-?\d*\.\d+$/.test(value)) throw new Error(`Invalid numeric value for slider input: "${value}". Expected a valid number (integer or float).`);
422
+ const numericValue = Number(value);
423
+ if (!Number.isFinite(numericValue)) throw new Error(`Invalid numeric value for slider input: "${value}". Value must be finite.`);
424
+ return `${sanitizedPythonVariableName} = ${numericValue}`;
425
+ }
426
+ function createPythonCodeForInputFileBlock(block) {
427
+ const sanitizedPythonVariableName = sanitizePythonVariableName(block.metadata.deepnote_variable_name);
428
+ if (block.metadata.deepnote_variable_value === "") return `${sanitizedPythonVariableName} = None`;
429
+ return `${sanitizedPythonVariableName} = ${escapePythonString(block.metadata.deepnote_variable_value)}`;
430
+ }
431
+ function createPythonCodeForInputDateBlock(block) {
432
+ const sanitizedPythonVariableName = sanitizePythonVariableName(block.metadata.deepnote_variable_name);
433
+ const escapedValue = escapePythonString(block.metadata.deepnote_variable_value);
434
+ if (!block.metadata.deepnote_variable_value) return `
435
+ ${sanitizedPythonVariableName} = None
436
+ `;
437
+ if (block.metadata.deepnote_input_date_version === 2) return `
438
+ from dateutil.parser import parse as _deepnote_parse
439
+ ${sanitizedPythonVariableName} = _deepnote_parse(${escapedValue}).date()
440
+ `;
441
+ return `
442
+ from datetime import datetime as _deepnote_datetime
443
+ ${sanitizedPythonVariableName} = _deepnote_datetime.strptime(${escapedValue}, "%Y-%m-%dT%H:%M:%S.%fZ")
444
+ `;
445
+ }
446
+ function createPythonCodeForInputDateRangeBlock(block) {
447
+ const sanitizedPythonVariableName = sanitizePythonVariableName(block.metadata.deepnote_variable_name);
448
+ if (isValidAbsoluteDateRange(block.metadata.deepnote_variable_value)) {
449
+ const startDate = block.metadata.deepnote_variable_value[0];
450
+ const endDate = block.metadata.deepnote_variable_value[1];
451
+ return pythonCode.dateRangeAbsolute(sanitizedPythonVariableName, startDate, endDate);
452
+ } else if (isCustomDateRange(block.metadata.deepnote_variable_value)) {
453
+ const customDays = block.metadata.deepnote_variable_value.replace("customDays", "");
454
+ return pythonCode.dateRangeCustomDays(sanitizedPythonVariableName, Number(customDays));
455
+ } else if (isValidRelativeDateInterval(block.metadata.deepnote_variable_value)) {
456
+ const range = DATE_RANGE_INPUT_RELATIVE_RANGES.find((range$1) => range$1.value === block.metadata.deepnote_variable_value);
457
+ if (!range) throw new Error(`Invalid relative date interval: "${block.metadata.deepnote_variable_value}". Expected one of: ${DATE_RANGE_INPUT_RELATIVE_RANGES.map((r) => r.value).join(", ")}.`);
458
+ return dedent`
459
+ ${range.pythonCode(sanitizedPythonVariableName)}`;
460
+ } else return dedent`
461
+ ${sanitizedPythonVariableName} = [None, None]
462
+ `;
463
+ }
464
+ function isInputTextBlock(block) {
465
+ return block.type === "input-text";
466
+ }
467
+ function isInputTextareaBlock(block) {
468
+ return block.type === "input-textarea";
469
+ }
470
+ function isInputCheckboxBlock(block) {
471
+ return block.type === "input-checkbox";
472
+ }
473
+ function isInputSelectBlock(block) {
474
+ return block.type === "input-select";
475
+ }
476
+ function isInputSliderBlock(block) {
477
+ return block.type === "input-slider";
478
+ }
479
+ function isInputFileBlock(block) {
480
+ return block.type === "input-file";
481
+ }
482
+ function isInputDateBlock(block) {
483
+ return block.type === "input-date";
484
+ }
485
+ function isInputDateRangeBlock(block) {
486
+ return block.type === "input-date-range";
487
+ }
488
+
489
+ //#endregion
490
+ //#region src/blocks/sql-utils.ts
491
+ function convertToEnvironmentVariableName(str) {
492
+ return (/^\d/.test(str) ? `_${str}` : str).toUpperCase().replace(/[^\w]/g, "_");
493
+ }
494
+ function getSqlEnvVarName(integrationId) {
495
+ return `SQL_${integrationId}`;
496
+ }
497
+
498
+ //#endregion
499
+ //#region src/blocks/sql-blocks.ts
500
+ function createPythonCodeForSqlBlock(block) {
501
+ const query = block.content;
502
+ const pythonVariableName = block.metadata.deepnote_variable_name;
503
+ const sanitizedPythonVariableName = pythonVariableName !== void 0 ? sanitizePythonVariableName(pythonVariableName) || "input_1" : void 0;
504
+ const returnVariableType = block.metadata.deepnote_return_variable_type ?? "dataframe";
505
+ const integrationId = block.metadata.sql_integration_id;
506
+ const connectionEnvVarName = integrationId ? convertToEnvironmentVariableName(getSqlEnvVarName(integrationId)) : "SQL_ALCHEMY_JSON_ENV_VAR";
507
+ const escapedQuery = escapePythonString(query);
508
+ const dataFrameConfig = createDataFrameConfig(block);
509
+ const executeSqlFunctionCall = dedent`
510
+ _dntk.execute_sql(
511
+ ${escapedQuery},
512
+ '${connectionEnvVarName}',
513
+ audit_sql_comment='',
514
+ sql_cache_mode='cache_disabled',
515
+ return_variable_type='${returnVariableType}'
516
+ )
517
+ `;
518
+ if (sanitizedPythonVariableName === void 0) return dedent`
519
+ ${dataFrameConfig}
520
+
521
+ ${executeSqlFunctionCall}
522
+ `;
523
+ return dedent`
524
+ ${dataFrameConfig}
525
+
526
+ ${sanitizedPythonVariableName} = ${executeSqlFunctionCall}
527
+ ${sanitizedPythonVariableName}
528
+ `;
529
+ }
530
+ function isSqlBlock(block) {
531
+ return block.type === "sql";
532
+ }
533
+
534
+ //#endregion
535
+ //#region src/blocks/visualization-blocks.ts
536
+ function createPythonCodeForVisualizationBlock(block) {
537
+ const variableName = block.metadata.deepnote_variable_name;
538
+ const spec = block.metadata.deepnote_visualization_spec;
539
+ const filters = block.metadata.deepnote_chart_filter?.advancedFilters ?? [];
540
+ if (!variableName || !spec) return "";
541
+ const sanitizedVariableName = sanitizePythonVariableName(variableName);
542
+ return pythonCode.executeVisualization(sanitizedVariableName, JSON.stringify(spec), JSON.stringify(filters));
543
+ }
544
+ function isVisualizationBlock(block) {
545
+ return block.type === "visualization";
546
+ }
547
+
548
+ //#endregion
549
+ //#region src/python-code.ts
550
+ function createPythonCode(block, executionContext) {
551
+ if (isCodeBlock(block)) return createPythonCodeForCodeBlock(block);
552
+ if (isSqlBlock(block)) return createPythonCodeForSqlBlock(block);
553
+ if (isInputTextBlock(block)) return createPythonCodeForInputTextBlock(block);
554
+ if (isInputTextareaBlock(block)) return createPythonCodeForInputTextareaBlock(block);
555
+ if (isInputCheckboxBlock(block)) return createPythonCodeForInputCheckboxBlock(block);
556
+ if (isInputSelectBlock(block)) return createPythonCodeForInputSelectBlock(block);
557
+ if (isInputSliderBlock(block)) return createPythonCodeForInputSliderBlock(block);
558
+ if (isInputFileBlock(block)) return createPythonCodeForInputFileBlock(block);
559
+ if (isInputDateBlock(block)) return createPythonCodeForInputDateBlock(block);
560
+ if (isInputDateRangeBlock(block)) return createPythonCodeForInputDateRangeBlock(block);
561
+ if (isVisualizationBlock(block)) return createPythonCodeForVisualizationBlock(block);
562
+ if (isButtonBlock(block)) return createPythonCodeForButtonBlock(block, executionContext);
563
+ if (isBigNumberBlock(block)) return createPythonCodeForBigNumberBlock(block);
564
+ throw new UnsupportedBlockTypeError(`Creating python code from block type ${block.type} is not supported yet.`);
565
+ }
566
+
567
+ //#endregion
568
+ export { createMarkdown, createPythonCode, deepnoteBlockSchema, deserializeDeepnoteFile, stripMarkdown };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@deepnote/blocks",
3
+ "version": "1.3.0",
4
+ "description": "",
5
+ "keywords": [],
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/deepnote/deepnote.git",
9
+ "directory": "packages/blocks"
10
+ },
11
+ "license": "Apache-2.0",
12
+ "type": "module",
13
+ "exports": {
14
+ ".": {
15
+ "import": "./dist/index.js",
16
+ "require": "./dist/index.cjs",
17
+ "types": "./dist/index.d.ts"
18
+ }
19
+ },
20
+ "main": "./dist/index.cjs",
21
+ "module": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "files": [
24
+ "dist",
25
+ "README.md",
26
+ "LICENSE",
27
+ "package.json"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsdown --format esm --format cjs --dts",
31
+ "test": "vitest",
32
+ "watch": "tsdown --watch --format esm --format cjs --dts"
33
+ },
34
+ "dependencies": {
35
+ "ts-dedent": "^2.2.0",
36
+ "yaml": "^2.8.1",
37
+ "zod": "3.25.76"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public",
41
+ "registry": "https://registry.npmjs.org"
42
+ }
43
+ }