@deepnote/blocks 1.3.5 → 1.4.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.cjs CHANGED
@@ -32,7 +32,10 @@ ts_dedent = __toESM(ts_dedent);
32
32
  const deepnoteBlockSchema = zod.z.object({
33
33
  blockGroup: zod.z.string().optional(),
34
34
  content: zod.z.string().optional(),
35
+ contentHash: zod.z.string().regex(/^(md5|sha256):[a-f0-9]+$/i).optional(),
35
36
  executionCount: zod.z.number().optional(),
37
+ executionFinishedAt: zod.z.string().datetime().optional(),
38
+ executionStartedAt: zod.z.string().datetime().optional(),
36
39
  id: zod.z.string(),
37
40
  metadata: zod.z.record(zod.z.any()).optional(),
38
41
  outputs: zod.z.array(zod.z.any()).optional(),
@@ -40,7 +43,49 @@ const deepnoteBlockSchema = zod.z.object({
40
43
  type: zod.z.string(),
41
44
  version: zod.z.number().optional()
42
45
  });
46
+ const environmentSchema = zod.z.object({
47
+ customImage: zod.z.string().optional(),
48
+ hash: zod.z.string().optional(),
49
+ packages: zod.z.record(zod.z.string()).optional(),
50
+ platform: zod.z.string().optional(),
51
+ python: zod.z.object({
52
+ environment: zod.z.enum([
53
+ "uv",
54
+ "conda",
55
+ "venv",
56
+ "poetry",
57
+ "system"
58
+ ]).optional(),
59
+ version: zod.z.string().optional()
60
+ }).optional()
61
+ }).optional();
62
+ const executionSummarySchema = zod.z.object({
63
+ blocksExecuted: zod.z.number().int().nonnegative().optional(),
64
+ blocksFailed: zod.z.number().int().nonnegative().optional(),
65
+ blocksSucceeded: zod.z.number().int().nonnegative().optional(),
66
+ totalDurationMs: zod.z.number().nonnegative().optional()
67
+ }).optional();
68
+ const executionErrorSchema = zod.z.object({
69
+ message: zod.z.string().optional(),
70
+ name: zod.z.string().optional(),
71
+ traceback: zod.z.array(zod.z.string()).optional()
72
+ }).optional();
73
+ const executionSchema = zod.z.object({
74
+ error: executionErrorSchema,
75
+ finishedAt: zod.z.string().datetime().optional(),
76
+ inputs: zod.z.record(zod.z.unknown()).optional(),
77
+ startedAt: zod.z.string().datetime().optional(),
78
+ summary: executionSummarySchema,
79
+ triggeredBy: zod.z.enum([
80
+ "user",
81
+ "schedule",
82
+ "api",
83
+ "ci"
84
+ ]).optional()
85
+ }).optional();
43
86
  const deepnoteFileSchema = zod.z.object({
87
+ environment: environmentSchema,
88
+ execution: executionSchema,
44
89
  metadata: zod.z.object({
45
90
  checksum: zod.z.string().optional(),
46
91
  createdAt: zod.z.string(),
@@ -78,9 +123,88 @@ const deepnoteFileSchema = zod.z.object({
78
123
 
79
124
  //#endregion
80
125
  //#region src/deserialize-file/parse-yaml.ts
126
+ /**
127
+ * Validates UTF-8 encoding from raw bytes before decoding to string.
128
+ * This is the proper way to validate UTF-8 - check BEFORE decoding.
129
+ *
130
+ * @param bytes - Raw file bytes as Uint8Array
131
+ * @returns Decoded UTF-8 string without BOM
132
+ * @throws Error if BOM detected or invalid UTF-8 encoding
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const bytes = await fs.readFile('file.deepnote') // Returns Buffer/Uint8Array
137
+ * const yamlContent = decodeUtf8NoBom(bytes)
138
+ * const parsed = parseYaml(yamlContent)
139
+ * ```
140
+ */
141
+ function decodeUtf8NoBom(bytes) {
142
+ if (bytes.length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) throw new Error("UTF-8 BOM detected in Deepnote file - files must be UTF-8 without BOM");
143
+ try {
144
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
145
+ } catch {
146
+ throw new Error("Invalid UTF-8 encoding detected in Deepnote file");
147
+ }
148
+ }
149
+ /**
150
+ * Validates that a string doesn't start with BOM.
151
+ * Note: This is a fallback when only a string is available.
152
+ * By the time we have a JS string, invalid UTF-8 has already been handled during decoding.
153
+ * For proper UTF-8 validation, use decodeUtf8NoBom() on raw bytes before decoding.
154
+ *
155
+ * @param yamlContent - Already-decoded YAML string
156
+ * @throws Error if BOM prefix detected
157
+ */
158
+ function validateNoBomPrefix(yamlContent) {
159
+ if (yamlContent.charCodeAt(0) === 65279) throw new Error("UTF-8 BOM detected in Deepnote file - files must be UTF-8 without BOM");
160
+ }
161
+ /**
162
+ * Validates that the YAML document doesn't contain prohibited features:
163
+ * - No anchors/aliases (&anchor, *alias)
164
+ * - No merge keys (<<)
165
+ * - No custom tags (!tag)
166
+ */
167
+ function validateYamlStructure(yamlContent) {
168
+ if (/(?:^|\n)\s*(?:-\s+|[\w-]+:\s*)&\w+/.test(yamlContent)) throw new Error("YAML anchors (&) are not allowed in Deepnote files");
169
+ if (/(?:^|\n)\s*(?:-\s+|[\w-]+:\s*)\*\w+/.test(yamlContent)) throw new Error("YAML aliases (*) are not allowed in Deepnote files");
170
+ if (/<<:/.test(yamlContent)) throw new Error("YAML merge keys (<<) are not allowed in Deepnote files");
171
+ const matches = yamlContent.match(/(?:^|\n)\s*(?:-\s+|[\w-]+:\s*)(![\w/-]+)/gm);
172
+ if (matches) {
173
+ const tags = matches.map((m) => m.match(/(![\w/-]+)/)?.[1]).filter(Boolean);
174
+ if (tags.length > 0) throw new Error(`YAML tags are not allowed in Deepnote files: ${tags.join(", ")}`);
175
+ }
176
+ }
177
+ /**
178
+ * Parse and validate YAML document in a single pass.
179
+ * Checks for duplicate keys and other parsing errors, then converts to JavaScript object.
180
+ */
181
+ function parseAndValidate(yamlContent) {
182
+ const doc = (0, yaml.parseDocument)(yamlContent, {
183
+ strict: true,
184
+ uniqueKeys: true,
185
+ version: "1.2"
186
+ });
187
+ if (doc.errors.length > 0) {
188
+ const duplicateKeyError = doc.errors.find((err) => err.message.includes("duplicate") || err.message.includes("key"));
189
+ if (duplicateKeyError) throw new Error(`Duplicate keys detected in Deepnote file: ${duplicateKeyError.message}`);
190
+ throw new Error(`YAML parsing error: ${doc.errors[0].message}`);
191
+ }
192
+ return doc.toJS();
193
+ }
194
+ /**
195
+ * Parse YAML content with strict validation rules:
196
+ * - YAML 1.2 only
197
+ * - UTF-8 only
198
+ * - No duplicate keys
199
+ * - No anchors/aliases/merge keys
200
+ * - No custom tags
201
+ * - Explicit typing enforced by schema
202
+ */
81
203
  function parseYaml(yamlContent) {
82
204
  try {
83
- return (0, yaml.parse)(yamlContent);
205
+ validateNoBomPrefix(yamlContent);
206
+ validateYamlStructure(yamlContent);
207
+ return parseAndValidate(yamlContent);
84
208
  } catch (error) {
85
209
  const message = error instanceof Error ? error.message : String(error);
86
210
  throw new Error(`Failed to parse Deepnote file: ${message}`);
@@ -593,6 +717,12 @@ function createPythonCode(block, executionContext) {
593
717
  //#endregion
594
718
  exports.createMarkdown = createMarkdown;
595
719
  exports.createPythonCode = createPythonCode;
720
+ exports.decodeUtf8NoBom = decodeUtf8NoBom;
596
721
  exports.deepnoteBlockSchema = deepnoteBlockSchema;
722
+ exports.deepnoteFileSchema = deepnoteFileSchema;
597
723
  exports.deserializeDeepnoteFile = deserializeDeepnoteFile;
724
+ exports.environmentSchema = environmentSchema;
725
+ exports.executionErrorSchema = executionErrorSchema;
726
+ exports.executionSchema = executionSchema;
727
+ exports.executionSummarySchema = executionSummarySchema;
598
728
  exports.stripMarkdown = stripMarkdown;
package/dist/index.d.cts CHANGED
@@ -30,7 +30,10 @@ interface TableState {
30
30
  declare const deepnoteBlockSchema: z.ZodObject<{
31
31
  blockGroup: z.ZodOptional<z.ZodString>;
32
32
  content: z.ZodOptional<z.ZodString>;
33
+ contentHash: z.ZodOptional<z.ZodString>;
33
34
  executionCount: z.ZodOptional<z.ZodNumber>;
35
+ executionFinishedAt: z.ZodOptional<z.ZodString>;
36
+ executionStartedAt: z.ZodOptional<z.ZodString>;
34
37
  id: z.ZodString;
35
38
  metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
36
39
  outputs: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
@@ -43,7 +46,10 @@ declare const deepnoteBlockSchema: z.ZodObject<{
43
46
  sortingKey: string;
44
47
  blockGroup?: string | undefined;
45
48
  content?: string | undefined;
49
+ contentHash?: string | undefined;
46
50
  executionCount?: number | undefined;
51
+ executionFinishedAt?: string | undefined;
52
+ executionStartedAt?: string | undefined;
47
53
  metadata?: Record<string, any> | undefined;
48
54
  outputs?: any[] | undefined;
49
55
  version?: number | undefined;
@@ -53,13 +59,251 @@ declare const deepnoteBlockSchema: z.ZodObject<{
53
59
  sortingKey: string;
54
60
  blockGroup?: string | undefined;
55
61
  content?: string | undefined;
62
+ contentHash?: string | undefined;
56
63
  executionCount?: number | undefined;
64
+ executionFinishedAt?: string | undefined;
65
+ executionStartedAt?: string | undefined;
57
66
  metadata?: Record<string, any> | undefined;
58
67
  outputs?: any[] | undefined;
59
68
  version?: number | undefined;
60
69
  }>;
61
70
  type DeepnoteBlock = z.infer<typeof deepnoteBlockSchema>;
71
+ declare const environmentSchema: z.ZodOptional<z.ZodObject<{
72
+ customImage: z.ZodOptional<z.ZodString>;
73
+ hash: z.ZodOptional<z.ZodString>;
74
+ packages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
75
+ platform: z.ZodOptional<z.ZodString>;
76
+ python: z.ZodOptional<z.ZodObject<{
77
+ environment: z.ZodOptional<z.ZodEnum<["uv", "conda", "venv", "poetry", "system"]>>;
78
+ version: z.ZodOptional<z.ZodString>;
79
+ }, "strip", z.ZodTypeAny, {
80
+ version?: string | undefined;
81
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
82
+ }, {
83
+ version?: string | undefined;
84
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
85
+ }>>;
86
+ }, "strip", z.ZodTypeAny, {
87
+ customImage?: string | undefined;
88
+ hash?: string | undefined;
89
+ packages?: Record<string, string> | undefined;
90
+ platform?: string | undefined;
91
+ python?: {
92
+ version?: string | undefined;
93
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
94
+ } | undefined;
95
+ }, {
96
+ customImage?: string | undefined;
97
+ hash?: string | undefined;
98
+ packages?: Record<string, string> | undefined;
99
+ platform?: string | undefined;
100
+ python?: {
101
+ version?: string | undefined;
102
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
103
+ } | undefined;
104
+ }>>;
105
+ type Environment = z.infer<typeof environmentSchema>;
106
+ declare const executionSummarySchema: z.ZodOptional<z.ZodObject<{
107
+ blocksExecuted: z.ZodOptional<z.ZodNumber>;
108
+ blocksFailed: z.ZodOptional<z.ZodNumber>;
109
+ blocksSucceeded: z.ZodOptional<z.ZodNumber>;
110
+ totalDurationMs: z.ZodOptional<z.ZodNumber>;
111
+ }, "strip", z.ZodTypeAny, {
112
+ blocksExecuted?: number | undefined;
113
+ blocksFailed?: number | undefined;
114
+ blocksSucceeded?: number | undefined;
115
+ totalDurationMs?: number | undefined;
116
+ }, {
117
+ blocksExecuted?: number | undefined;
118
+ blocksFailed?: number | undefined;
119
+ blocksSucceeded?: number | undefined;
120
+ totalDurationMs?: number | undefined;
121
+ }>>;
122
+ type ExecutionSummary = z.infer<typeof executionSummarySchema>;
123
+ declare const executionErrorSchema: z.ZodOptional<z.ZodObject<{
124
+ message: z.ZodOptional<z.ZodString>;
125
+ name: z.ZodOptional<z.ZodString>;
126
+ traceback: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
127
+ }, "strip", z.ZodTypeAny, {
128
+ message?: string | undefined;
129
+ name?: string | undefined;
130
+ traceback?: string[] | undefined;
131
+ }, {
132
+ message?: string | undefined;
133
+ name?: string | undefined;
134
+ traceback?: string[] | undefined;
135
+ }>>;
136
+ type ExecutionError = z.infer<typeof executionErrorSchema>;
137
+ declare const executionSchema: z.ZodOptional<z.ZodObject<{
138
+ error: z.ZodOptional<z.ZodObject<{
139
+ message: z.ZodOptional<z.ZodString>;
140
+ name: z.ZodOptional<z.ZodString>;
141
+ traceback: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
142
+ }, "strip", z.ZodTypeAny, {
143
+ message?: string | undefined;
144
+ name?: string | undefined;
145
+ traceback?: string[] | undefined;
146
+ }, {
147
+ message?: string | undefined;
148
+ name?: string | undefined;
149
+ traceback?: string[] | undefined;
150
+ }>>;
151
+ finishedAt: z.ZodOptional<z.ZodString>;
152
+ inputs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
153
+ startedAt: z.ZodOptional<z.ZodString>;
154
+ summary: z.ZodOptional<z.ZodObject<{
155
+ blocksExecuted: z.ZodOptional<z.ZodNumber>;
156
+ blocksFailed: z.ZodOptional<z.ZodNumber>;
157
+ blocksSucceeded: z.ZodOptional<z.ZodNumber>;
158
+ totalDurationMs: z.ZodOptional<z.ZodNumber>;
159
+ }, "strip", z.ZodTypeAny, {
160
+ blocksExecuted?: number | undefined;
161
+ blocksFailed?: number | undefined;
162
+ blocksSucceeded?: number | undefined;
163
+ totalDurationMs?: number | undefined;
164
+ }, {
165
+ blocksExecuted?: number | undefined;
166
+ blocksFailed?: number | undefined;
167
+ blocksSucceeded?: number | undefined;
168
+ totalDurationMs?: number | undefined;
169
+ }>>;
170
+ triggeredBy: z.ZodOptional<z.ZodEnum<["user", "schedule", "api", "ci"]>>;
171
+ }, "strip", z.ZodTypeAny, {
172
+ error?: {
173
+ message?: string | undefined;
174
+ name?: string | undefined;
175
+ traceback?: string[] | undefined;
176
+ } | undefined;
177
+ finishedAt?: string | undefined;
178
+ inputs?: Record<string, unknown> | undefined;
179
+ startedAt?: string | undefined;
180
+ summary?: {
181
+ blocksExecuted?: number | undefined;
182
+ blocksFailed?: number | undefined;
183
+ blocksSucceeded?: number | undefined;
184
+ totalDurationMs?: number | undefined;
185
+ } | undefined;
186
+ triggeredBy?: "user" | "schedule" | "api" | "ci" | undefined;
187
+ }, {
188
+ error?: {
189
+ message?: string | undefined;
190
+ name?: string | undefined;
191
+ traceback?: string[] | undefined;
192
+ } | undefined;
193
+ finishedAt?: string | undefined;
194
+ inputs?: Record<string, unknown> | undefined;
195
+ startedAt?: string | undefined;
196
+ summary?: {
197
+ blocksExecuted?: number | undefined;
198
+ blocksFailed?: number | undefined;
199
+ blocksSucceeded?: number | undefined;
200
+ totalDurationMs?: number | undefined;
201
+ } | undefined;
202
+ triggeredBy?: "user" | "schedule" | "api" | "ci" | undefined;
203
+ }>>;
204
+ type Execution = z.infer<typeof executionSchema>;
62
205
  declare const deepnoteFileSchema: z.ZodObject<{
206
+ environment: z.ZodOptional<z.ZodObject<{
207
+ customImage: z.ZodOptional<z.ZodString>;
208
+ hash: z.ZodOptional<z.ZodString>;
209
+ packages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
210
+ platform: z.ZodOptional<z.ZodString>;
211
+ python: z.ZodOptional<z.ZodObject<{
212
+ environment: z.ZodOptional<z.ZodEnum<["uv", "conda", "venv", "poetry", "system"]>>;
213
+ version: z.ZodOptional<z.ZodString>;
214
+ }, "strip", z.ZodTypeAny, {
215
+ version?: string | undefined;
216
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
217
+ }, {
218
+ version?: string | undefined;
219
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
220
+ }>>;
221
+ }, "strip", z.ZodTypeAny, {
222
+ customImage?: string | undefined;
223
+ hash?: string | undefined;
224
+ packages?: Record<string, string> | undefined;
225
+ platform?: string | undefined;
226
+ python?: {
227
+ version?: string | undefined;
228
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
229
+ } | undefined;
230
+ }, {
231
+ customImage?: string | undefined;
232
+ hash?: string | undefined;
233
+ packages?: Record<string, string> | undefined;
234
+ platform?: string | undefined;
235
+ python?: {
236
+ version?: string | undefined;
237
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
238
+ } | undefined;
239
+ }>>;
240
+ execution: z.ZodOptional<z.ZodObject<{
241
+ error: z.ZodOptional<z.ZodObject<{
242
+ message: z.ZodOptional<z.ZodString>;
243
+ name: z.ZodOptional<z.ZodString>;
244
+ traceback: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
245
+ }, "strip", z.ZodTypeAny, {
246
+ message?: string | undefined;
247
+ name?: string | undefined;
248
+ traceback?: string[] | undefined;
249
+ }, {
250
+ message?: string | undefined;
251
+ name?: string | undefined;
252
+ traceback?: string[] | undefined;
253
+ }>>;
254
+ finishedAt: z.ZodOptional<z.ZodString>;
255
+ inputs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
256
+ startedAt: z.ZodOptional<z.ZodString>;
257
+ summary: z.ZodOptional<z.ZodObject<{
258
+ blocksExecuted: z.ZodOptional<z.ZodNumber>;
259
+ blocksFailed: z.ZodOptional<z.ZodNumber>;
260
+ blocksSucceeded: z.ZodOptional<z.ZodNumber>;
261
+ totalDurationMs: z.ZodOptional<z.ZodNumber>;
262
+ }, "strip", z.ZodTypeAny, {
263
+ blocksExecuted?: number | undefined;
264
+ blocksFailed?: number | undefined;
265
+ blocksSucceeded?: number | undefined;
266
+ totalDurationMs?: number | undefined;
267
+ }, {
268
+ blocksExecuted?: number | undefined;
269
+ blocksFailed?: number | undefined;
270
+ blocksSucceeded?: number | undefined;
271
+ totalDurationMs?: number | undefined;
272
+ }>>;
273
+ triggeredBy: z.ZodOptional<z.ZodEnum<["user", "schedule", "api", "ci"]>>;
274
+ }, "strip", z.ZodTypeAny, {
275
+ error?: {
276
+ message?: string | undefined;
277
+ name?: string | undefined;
278
+ traceback?: string[] | undefined;
279
+ } | undefined;
280
+ finishedAt?: string | undefined;
281
+ inputs?: Record<string, unknown> | undefined;
282
+ startedAt?: string | undefined;
283
+ summary?: {
284
+ blocksExecuted?: number | undefined;
285
+ blocksFailed?: number | undefined;
286
+ blocksSucceeded?: number | undefined;
287
+ totalDurationMs?: number | undefined;
288
+ } | undefined;
289
+ triggeredBy?: "user" | "schedule" | "api" | "ci" | undefined;
290
+ }, {
291
+ error?: {
292
+ message?: string | undefined;
293
+ name?: string | undefined;
294
+ traceback?: string[] | undefined;
295
+ } | undefined;
296
+ finishedAt?: string | undefined;
297
+ inputs?: Record<string, unknown> | undefined;
298
+ startedAt?: string | undefined;
299
+ summary?: {
300
+ blocksExecuted?: number | undefined;
301
+ blocksFailed?: number | undefined;
302
+ blocksSucceeded?: number | undefined;
303
+ totalDurationMs?: number | undefined;
304
+ } | undefined;
305
+ triggeredBy?: "user" | "schedule" | "api" | "ci" | undefined;
306
+ }>>;
63
307
  metadata: z.ZodObject<{
64
308
  checksum: z.ZodOptional<z.ZodString>;
65
309
  createdAt: z.ZodString;
@@ -97,7 +341,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
97
341
  blocks: z.ZodArray<z.ZodObject<{
98
342
  blockGroup: z.ZodOptional<z.ZodString>;
99
343
  content: z.ZodOptional<z.ZodString>;
344
+ contentHash: z.ZodOptional<z.ZodString>;
100
345
  executionCount: z.ZodOptional<z.ZodNumber>;
346
+ executionFinishedAt: z.ZodOptional<z.ZodString>;
347
+ executionStartedAt: z.ZodOptional<z.ZodString>;
101
348
  id: z.ZodString;
102
349
  metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
103
350
  outputs: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
@@ -110,7 +357,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
110
357
  sortingKey: string;
111
358
  blockGroup?: string | undefined;
112
359
  content?: string | undefined;
360
+ contentHash?: string | undefined;
113
361
  executionCount?: number | undefined;
362
+ executionFinishedAt?: string | undefined;
363
+ executionStartedAt?: string | undefined;
114
364
  metadata?: Record<string, any> | undefined;
115
365
  outputs?: any[] | undefined;
116
366
  version?: number | undefined;
@@ -120,7 +370,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
120
370
  sortingKey: string;
121
371
  blockGroup?: string | undefined;
122
372
  content?: string | undefined;
373
+ contentHash?: string | undefined;
123
374
  executionCount?: number | undefined;
375
+ executionFinishedAt?: string | undefined;
376
+ executionStartedAt?: string | undefined;
124
377
  metadata?: Record<string, any> | undefined;
125
378
  outputs?: any[] | undefined;
126
379
  version?: number | undefined;
@@ -139,7 +392,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
139
392
  sortingKey: string;
140
393
  blockGroup?: string | undefined;
141
394
  content?: string | undefined;
395
+ contentHash?: string | undefined;
142
396
  executionCount?: number | undefined;
397
+ executionFinishedAt?: string | undefined;
398
+ executionStartedAt?: string | undefined;
143
399
  metadata?: Record<string, any> | undefined;
144
400
  outputs?: any[] | undefined;
145
401
  version?: number | undefined;
@@ -156,7 +412,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
156
412
  sortingKey: string;
157
413
  blockGroup?: string | undefined;
158
414
  content?: string | undefined;
415
+ contentHash?: string | undefined;
159
416
  executionCount?: number | undefined;
417
+ executionFinishedAt?: string | undefined;
418
+ executionStartedAt?: string | undefined;
160
419
  metadata?: Record<string, any> | undefined;
161
420
  outputs?: any[] | undefined;
162
421
  version?: number | undefined;
@@ -166,6 +425,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
166
425
  workingDirectory?: string | undefined;
167
426
  }>, "many">;
168
427
  settings: z.ZodOptional<z.ZodObject<{
428
+ /**
429
+ * @deprecated Use top-level `environment` instead.
430
+ * This field is kept for backward compatibility.
431
+ */
169
432
  environment: z.ZodOptional<z.ZodObject<{
170
433
  customImage: z.ZodOptional<z.ZodString>;
171
434
  pythonVersion: z.ZodOptional<z.ZodString>;
@@ -205,7 +468,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
205
468
  sortingKey: string;
206
469
  blockGroup?: string | undefined;
207
470
  content?: string | undefined;
471
+ contentHash?: string | undefined;
208
472
  executionCount?: number | undefined;
473
+ executionFinishedAt?: string | undefined;
474
+ executionStartedAt?: string | undefined;
209
475
  metadata?: Record<string, any> | undefined;
210
476
  outputs?: any[] | undefined;
211
477
  version?: number | undefined;
@@ -240,7 +506,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
240
506
  sortingKey: string;
241
507
  blockGroup?: string | undefined;
242
508
  content?: string | undefined;
509
+ contentHash?: string | undefined;
243
510
  executionCount?: number | undefined;
511
+ executionFinishedAt?: string | undefined;
512
+ executionStartedAt?: string | undefined;
244
513
  metadata?: Record<string, any> | undefined;
245
514
  outputs?: any[] | undefined;
246
515
  version?: number | undefined;
@@ -285,7 +554,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
285
554
  sortingKey: string;
286
555
  blockGroup?: string | undefined;
287
556
  content?: string | undefined;
557
+ contentHash?: string | undefined;
288
558
  executionCount?: number | undefined;
559
+ executionFinishedAt?: string | undefined;
560
+ executionStartedAt?: string | undefined;
289
561
  metadata?: Record<string, any> | undefined;
290
562
  outputs?: any[] | undefined;
291
563
  version?: number | undefined;
@@ -309,6 +581,33 @@ declare const deepnoteFileSchema: z.ZodObject<{
309
581
  sqlCacheMaxAge?: number | undefined;
310
582
  } | undefined;
311
583
  };
584
+ environment?: {
585
+ customImage?: string | undefined;
586
+ hash?: string | undefined;
587
+ packages?: Record<string, string> | undefined;
588
+ platform?: string | undefined;
589
+ python?: {
590
+ version?: string | undefined;
591
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
592
+ } | undefined;
593
+ } | undefined;
594
+ execution?: {
595
+ error?: {
596
+ message?: string | undefined;
597
+ name?: string | undefined;
598
+ traceback?: string[] | undefined;
599
+ } | undefined;
600
+ finishedAt?: string | undefined;
601
+ inputs?: Record<string, unknown> | undefined;
602
+ startedAt?: string | undefined;
603
+ summary?: {
604
+ blocksExecuted?: number | undefined;
605
+ blocksFailed?: number | undefined;
606
+ blocksSucceeded?: number | undefined;
607
+ totalDurationMs?: number | undefined;
608
+ } | undefined;
609
+ triggeredBy?: "user" | "schedule" | "api" | "ci" | undefined;
610
+ } | undefined;
312
611
  }, {
313
612
  metadata: {
314
613
  createdAt: string;
@@ -329,7 +628,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
329
628
  sortingKey: string;
330
629
  blockGroup?: string | undefined;
331
630
  content?: string | undefined;
631
+ contentHash?: string | undefined;
332
632
  executionCount?: number | undefined;
633
+ executionFinishedAt?: string | undefined;
634
+ executionStartedAt?: string | undefined;
333
635
  metadata?: Record<string, any> | undefined;
334
636
  outputs?: any[] | undefined;
335
637
  version?: number | undefined;
@@ -353,6 +655,33 @@ declare const deepnoteFileSchema: z.ZodObject<{
353
655
  sqlCacheMaxAge?: number | undefined;
354
656
  } | undefined;
355
657
  };
658
+ environment?: {
659
+ customImage?: string | undefined;
660
+ hash?: string | undefined;
661
+ packages?: Record<string, string> | undefined;
662
+ platform?: string | undefined;
663
+ python?: {
664
+ version?: string | undefined;
665
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
666
+ } | undefined;
667
+ } | undefined;
668
+ execution?: {
669
+ error?: {
670
+ message?: string | undefined;
671
+ name?: string | undefined;
672
+ traceback?: string[] | undefined;
673
+ } | undefined;
674
+ finishedAt?: string | undefined;
675
+ inputs?: Record<string, unknown> | undefined;
676
+ startedAt?: string | undefined;
677
+ summary?: {
678
+ blocksExecuted?: number | undefined;
679
+ blocksFailed?: number | undefined;
680
+ blocksSucceeded?: number | undefined;
681
+ totalDurationMs?: number | undefined;
682
+ } | undefined;
683
+ triggeredBy?: "user" | "schedule" | "api" | "ci" | undefined;
684
+ } | undefined;
356
685
  }>;
357
686
  type DeepnoteFile = z.infer<typeof deepnoteFileSchema>;
358
687
  //#endregion
@@ -362,6 +691,24 @@ type DeepnoteFile = z.infer<typeof deepnoteFileSchema>;
362
691
  */
363
692
  declare function deserializeDeepnoteFile(yamlContent: string): DeepnoteFile;
364
693
  //#endregion
694
+ //#region src/deserialize-file/parse-yaml.d.ts
695
+ /**
696
+ * Validates UTF-8 encoding from raw bytes before decoding to string.
697
+ * This is the proper way to validate UTF-8 - check BEFORE decoding.
698
+ *
699
+ * @param bytes - Raw file bytes as Uint8Array
700
+ * @returns Decoded UTF-8 string without BOM
701
+ * @throws Error if BOM detected or invalid UTF-8 encoding
702
+ *
703
+ * @example
704
+ * ```typescript
705
+ * const bytes = await fs.readFile('file.deepnote') // Returns Buffer/Uint8Array
706
+ * const yamlContent = decodeUtf8NoBom(bytes)
707
+ * const parsed = parseYaml(yamlContent)
708
+ * ```
709
+ */
710
+ declare function decodeUtf8NoBom(bytes: Uint8Array): string;
711
+ //#endregion
365
712
  //#region src/markdown.d.ts
366
713
  declare function createMarkdown(block: DeepnoteBlock): string;
367
714
  declare function stripMarkdown(block: DeepnoteBlock): string;
@@ -378,4 +725,4 @@ interface ButtonExecutionContext {
378
725
  //#region src/python-code.d.ts
379
726
  declare function createPythonCode(block: DeepnoteBlock, executionContext?: ButtonExecutionContext): string;
380
727
  //#endregion
381
- export { type DeepnoteBlock, type DeepnoteFile, type TableState, createMarkdown, createPythonCode, deepnoteBlockSchema, deserializeDeepnoteFile, stripMarkdown };
728
+ export { type DeepnoteBlock, type DeepnoteFile, type Environment, type Execution, type ExecutionError, type ExecutionSummary, type TableState, createMarkdown, createPythonCode, decodeUtf8NoBom, deepnoteBlockSchema, deepnoteFileSchema, deserializeDeepnoteFile, environmentSchema, executionErrorSchema, executionSchema, executionSummarySchema, stripMarkdown };
package/dist/index.d.ts CHANGED
@@ -30,7 +30,10 @@ interface TableState {
30
30
  declare const deepnoteBlockSchema: z.ZodObject<{
31
31
  blockGroup: z.ZodOptional<z.ZodString>;
32
32
  content: z.ZodOptional<z.ZodString>;
33
+ contentHash: z.ZodOptional<z.ZodString>;
33
34
  executionCount: z.ZodOptional<z.ZodNumber>;
35
+ executionFinishedAt: z.ZodOptional<z.ZodString>;
36
+ executionStartedAt: z.ZodOptional<z.ZodString>;
34
37
  id: z.ZodString;
35
38
  metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
36
39
  outputs: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
@@ -43,7 +46,10 @@ declare const deepnoteBlockSchema: z.ZodObject<{
43
46
  sortingKey: string;
44
47
  blockGroup?: string | undefined;
45
48
  content?: string | undefined;
49
+ contentHash?: string | undefined;
46
50
  executionCount?: number | undefined;
51
+ executionFinishedAt?: string | undefined;
52
+ executionStartedAt?: string | undefined;
47
53
  metadata?: Record<string, any> | undefined;
48
54
  outputs?: any[] | undefined;
49
55
  version?: number | undefined;
@@ -53,13 +59,251 @@ declare const deepnoteBlockSchema: z.ZodObject<{
53
59
  sortingKey: string;
54
60
  blockGroup?: string | undefined;
55
61
  content?: string | undefined;
62
+ contentHash?: string | undefined;
56
63
  executionCount?: number | undefined;
64
+ executionFinishedAt?: string | undefined;
65
+ executionStartedAt?: string | undefined;
57
66
  metadata?: Record<string, any> | undefined;
58
67
  outputs?: any[] | undefined;
59
68
  version?: number | undefined;
60
69
  }>;
61
70
  type DeepnoteBlock = z.infer<typeof deepnoteBlockSchema>;
71
+ declare const environmentSchema: z.ZodOptional<z.ZodObject<{
72
+ customImage: z.ZodOptional<z.ZodString>;
73
+ hash: z.ZodOptional<z.ZodString>;
74
+ packages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
75
+ platform: z.ZodOptional<z.ZodString>;
76
+ python: z.ZodOptional<z.ZodObject<{
77
+ environment: z.ZodOptional<z.ZodEnum<["uv", "conda", "venv", "poetry", "system"]>>;
78
+ version: z.ZodOptional<z.ZodString>;
79
+ }, "strip", z.ZodTypeAny, {
80
+ version?: string | undefined;
81
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
82
+ }, {
83
+ version?: string | undefined;
84
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
85
+ }>>;
86
+ }, "strip", z.ZodTypeAny, {
87
+ customImage?: string | undefined;
88
+ hash?: string | undefined;
89
+ packages?: Record<string, string> | undefined;
90
+ platform?: string | undefined;
91
+ python?: {
92
+ version?: string | undefined;
93
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
94
+ } | undefined;
95
+ }, {
96
+ customImage?: string | undefined;
97
+ hash?: string | undefined;
98
+ packages?: Record<string, string> | undefined;
99
+ platform?: string | undefined;
100
+ python?: {
101
+ version?: string | undefined;
102
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
103
+ } | undefined;
104
+ }>>;
105
+ type Environment = z.infer<typeof environmentSchema>;
106
+ declare const executionSummarySchema: z.ZodOptional<z.ZodObject<{
107
+ blocksExecuted: z.ZodOptional<z.ZodNumber>;
108
+ blocksFailed: z.ZodOptional<z.ZodNumber>;
109
+ blocksSucceeded: z.ZodOptional<z.ZodNumber>;
110
+ totalDurationMs: z.ZodOptional<z.ZodNumber>;
111
+ }, "strip", z.ZodTypeAny, {
112
+ blocksExecuted?: number | undefined;
113
+ blocksFailed?: number | undefined;
114
+ blocksSucceeded?: number | undefined;
115
+ totalDurationMs?: number | undefined;
116
+ }, {
117
+ blocksExecuted?: number | undefined;
118
+ blocksFailed?: number | undefined;
119
+ blocksSucceeded?: number | undefined;
120
+ totalDurationMs?: number | undefined;
121
+ }>>;
122
+ type ExecutionSummary = z.infer<typeof executionSummarySchema>;
123
+ declare const executionErrorSchema: z.ZodOptional<z.ZodObject<{
124
+ message: z.ZodOptional<z.ZodString>;
125
+ name: z.ZodOptional<z.ZodString>;
126
+ traceback: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
127
+ }, "strip", z.ZodTypeAny, {
128
+ message?: string | undefined;
129
+ name?: string | undefined;
130
+ traceback?: string[] | undefined;
131
+ }, {
132
+ message?: string | undefined;
133
+ name?: string | undefined;
134
+ traceback?: string[] | undefined;
135
+ }>>;
136
+ type ExecutionError = z.infer<typeof executionErrorSchema>;
137
+ declare const executionSchema: z.ZodOptional<z.ZodObject<{
138
+ error: z.ZodOptional<z.ZodObject<{
139
+ message: z.ZodOptional<z.ZodString>;
140
+ name: z.ZodOptional<z.ZodString>;
141
+ traceback: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
142
+ }, "strip", z.ZodTypeAny, {
143
+ message?: string | undefined;
144
+ name?: string | undefined;
145
+ traceback?: string[] | undefined;
146
+ }, {
147
+ message?: string | undefined;
148
+ name?: string | undefined;
149
+ traceback?: string[] | undefined;
150
+ }>>;
151
+ finishedAt: z.ZodOptional<z.ZodString>;
152
+ inputs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
153
+ startedAt: z.ZodOptional<z.ZodString>;
154
+ summary: z.ZodOptional<z.ZodObject<{
155
+ blocksExecuted: z.ZodOptional<z.ZodNumber>;
156
+ blocksFailed: z.ZodOptional<z.ZodNumber>;
157
+ blocksSucceeded: z.ZodOptional<z.ZodNumber>;
158
+ totalDurationMs: z.ZodOptional<z.ZodNumber>;
159
+ }, "strip", z.ZodTypeAny, {
160
+ blocksExecuted?: number | undefined;
161
+ blocksFailed?: number | undefined;
162
+ blocksSucceeded?: number | undefined;
163
+ totalDurationMs?: number | undefined;
164
+ }, {
165
+ blocksExecuted?: number | undefined;
166
+ blocksFailed?: number | undefined;
167
+ blocksSucceeded?: number | undefined;
168
+ totalDurationMs?: number | undefined;
169
+ }>>;
170
+ triggeredBy: z.ZodOptional<z.ZodEnum<["user", "schedule", "api", "ci"]>>;
171
+ }, "strip", z.ZodTypeAny, {
172
+ error?: {
173
+ message?: string | undefined;
174
+ name?: string | undefined;
175
+ traceback?: string[] | undefined;
176
+ } | undefined;
177
+ finishedAt?: string | undefined;
178
+ inputs?: Record<string, unknown> | undefined;
179
+ startedAt?: string | undefined;
180
+ summary?: {
181
+ blocksExecuted?: number | undefined;
182
+ blocksFailed?: number | undefined;
183
+ blocksSucceeded?: number | undefined;
184
+ totalDurationMs?: number | undefined;
185
+ } | undefined;
186
+ triggeredBy?: "user" | "schedule" | "api" | "ci" | undefined;
187
+ }, {
188
+ error?: {
189
+ message?: string | undefined;
190
+ name?: string | undefined;
191
+ traceback?: string[] | undefined;
192
+ } | undefined;
193
+ finishedAt?: string | undefined;
194
+ inputs?: Record<string, unknown> | undefined;
195
+ startedAt?: string | undefined;
196
+ summary?: {
197
+ blocksExecuted?: number | undefined;
198
+ blocksFailed?: number | undefined;
199
+ blocksSucceeded?: number | undefined;
200
+ totalDurationMs?: number | undefined;
201
+ } | undefined;
202
+ triggeredBy?: "user" | "schedule" | "api" | "ci" | undefined;
203
+ }>>;
204
+ type Execution = z.infer<typeof executionSchema>;
62
205
  declare const deepnoteFileSchema: z.ZodObject<{
206
+ environment: z.ZodOptional<z.ZodObject<{
207
+ customImage: z.ZodOptional<z.ZodString>;
208
+ hash: z.ZodOptional<z.ZodString>;
209
+ packages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
210
+ platform: z.ZodOptional<z.ZodString>;
211
+ python: z.ZodOptional<z.ZodObject<{
212
+ environment: z.ZodOptional<z.ZodEnum<["uv", "conda", "venv", "poetry", "system"]>>;
213
+ version: z.ZodOptional<z.ZodString>;
214
+ }, "strip", z.ZodTypeAny, {
215
+ version?: string | undefined;
216
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
217
+ }, {
218
+ version?: string | undefined;
219
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
220
+ }>>;
221
+ }, "strip", z.ZodTypeAny, {
222
+ customImage?: string | undefined;
223
+ hash?: string | undefined;
224
+ packages?: Record<string, string> | undefined;
225
+ platform?: string | undefined;
226
+ python?: {
227
+ version?: string | undefined;
228
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
229
+ } | undefined;
230
+ }, {
231
+ customImage?: string | undefined;
232
+ hash?: string | undefined;
233
+ packages?: Record<string, string> | undefined;
234
+ platform?: string | undefined;
235
+ python?: {
236
+ version?: string | undefined;
237
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
238
+ } | undefined;
239
+ }>>;
240
+ execution: z.ZodOptional<z.ZodObject<{
241
+ error: z.ZodOptional<z.ZodObject<{
242
+ message: z.ZodOptional<z.ZodString>;
243
+ name: z.ZodOptional<z.ZodString>;
244
+ traceback: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
245
+ }, "strip", z.ZodTypeAny, {
246
+ message?: string | undefined;
247
+ name?: string | undefined;
248
+ traceback?: string[] | undefined;
249
+ }, {
250
+ message?: string | undefined;
251
+ name?: string | undefined;
252
+ traceback?: string[] | undefined;
253
+ }>>;
254
+ finishedAt: z.ZodOptional<z.ZodString>;
255
+ inputs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
256
+ startedAt: z.ZodOptional<z.ZodString>;
257
+ summary: z.ZodOptional<z.ZodObject<{
258
+ blocksExecuted: z.ZodOptional<z.ZodNumber>;
259
+ blocksFailed: z.ZodOptional<z.ZodNumber>;
260
+ blocksSucceeded: z.ZodOptional<z.ZodNumber>;
261
+ totalDurationMs: z.ZodOptional<z.ZodNumber>;
262
+ }, "strip", z.ZodTypeAny, {
263
+ blocksExecuted?: number | undefined;
264
+ blocksFailed?: number | undefined;
265
+ blocksSucceeded?: number | undefined;
266
+ totalDurationMs?: number | undefined;
267
+ }, {
268
+ blocksExecuted?: number | undefined;
269
+ blocksFailed?: number | undefined;
270
+ blocksSucceeded?: number | undefined;
271
+ totalDurationMs?: number | undefined;
272
+ }>>;
273
+ triggeredBy: z.ZodOptional<z.ZodEnum<["user", "schedule", "api", "ci"]>>;
274
+ }, "strip", z.ZodTypeAny, {
275
+ error?: {
276
+ message?: string | undefined;
277
+ name?: string | undefined;
278
+ traceback?: string[] | undefined;
279
+ } | undefined;
280
+ finishedAt?: string | undefined;
281
+ inputs?: Record<string, unknown> | undefined;
282
+ startedAt?: string | undefined;
283
+ summary?: {
284
+ blocksExecuted?: number | undefined;
285
+ blocksFailed?: number | undefined;
286
+ blocksSucceeded?: number | undefined;
287
+ totalDurationMs?: number | undefined;
288
+ } | undefined;
289
+ triggeredBy?: "user" | "schedule" | "api" | "ci" | undefined;
290
+ }, {
291
+ error?: {
292
+ message?: string | undefined;
293
+ name?: string | undefined;
294
+ traceback?: string[] | undefined;
295
+ } | undefined;
296
+ finishedAt?: string | undefined;
297
+ inputs?: Record<string, unknown> | undefined;
298
+ startedAt?: string | undefined;
299
+ summary?: {
300
+ blocksExecuted?: number | undefined;
301
+ blocksFailed?: number | undefined;
302
+ blocksSucceeded?: number | undefined;
303
+ totalDurationMs?: number | undefined;
304
+ } | undefined;
305
+ triggeredBy?: "user" | "schedule" | "api" | "ci" | undefined;
306
+ }>>;
63
307
  metadata: z.ZodObject<{
64
308
  checksum: z.ZodOptional<z.ZodString>;
65
309
  createdAt: z.ZodString;
@@ -97,7 +341,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
97
341
  blocks: z.ZodArray<z.ZodObject<{
98
342
  blockGroup: z.ZodOptional<z.ZodString>;
99
343
  content: z.ZodOptional<z.ZodString>;
344
+ contentHash: z.ZodOptional<z.ZodString>;
100
345
  executionCount: z.ZodOptional<z.ZodNumber>;
346
+ executionFinishedAt: z.ZodOptional<z.ZodString>;
347
+ executionStartedAt: z.ZodOptional<z.ZodString>;
101
348
  id: z.ZodString;
102
349
  metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
103
350
  outputs: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
@@ -110,7 +357,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
110
357
  sortingKey: string;
111
358
  blockGroup?: string | undefined;
112
359
  content?: string | undefined;
360
+ contentHash?: string | undefined;
113
361
  executionCount?: number | undefined;
362
+ executionFinishedAt?: string | undefined;
363
+ executionStartedAt?: string | undefined;
114
364
  metadata?: Record<string, any> | undefined;
115
365
  outputs?: any[] | undefined;
116
366
  version?: number | undefined;
@@ -120,7 +370,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
120
370
  sortingKey: string;
121
371
  blockGroup?: string | undefined;
122
372
  content?: string | undefined;
373
+ contentHash?: string | undefined;
123
374
  executionCount?: number | undefined;
375
+ executionFinishedAt?: string | undefined;
376
+ executionStartedAt?: string | undefined;
124
377
  metadata?: Record<string, any> | undefined;
125
378
  outputs?: any[] | undefined;
126
379
  version?: number | undefined;
@@ -139,7 +392,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
139
392
  sortingKey: string;
140
393
  blockGroup?: string | undefined;
141
394
  content?: string | undefined;
395
+ contentHash?: string | undefined;
142
396
  executionCount?: number | undefined;
397
+ executionFinishedAt?: string | undefined;
398
+ executionStartedAt?: string | undefined;
143
399
  metadata?: Record<string, any> | undefined;
144
400
  outputs?: any[] | undefined;
145
401
  version?: number | undefined;
@@ -156,7 +412,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
156
412
  sortingKey: string;
157
413
  blockGroup?: string | undefined;
158
414
  content?: string | undefined;
415
+ contentHash?: string | undefined;
159
416
  executionCount?: number | undefined;
417
+ executionFinishedAt?: string | undefined;
418
+ executionStartedAt?: string | undefined;
160
419
  metadata?: Record<string, any> | undefined;
161
420
  outputs?: any[] | undefined;
162
421
  version?: number | undefined;
@@ -166,6 +425,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
166
425
  workingDirectory?: string | undefined;
167
426
  }>, "many">;
168
427
  settings: z.ZodOptional<z.ZodObject<{
428
+ /**
429
+ * @deprecated Use top-level `environment` instead.
430
+ * This field is kept for backward compatibility.
431
+ */
169
432
  environment: z.ZodOptional<z.ZodObject<{
170
433
  customImage: z.ZodOptional<z.ZodString>;
171
434
  pythonVersion: z.ZodOptional<z.ZodString>;
@@ -205,7 +468,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
205
468
  sortingKey: string;
206
469
  blockGroup?: string | undefined;
207
470
  content?: string | undefined;
471
+ contentHash?: string | undefined;
208
472
  executionCount?: number | undefined;
473
+ executionFinishedAt?: string | undefined;
474
+ executionStartedAt?: string | undefined;
209
475
  metadata?: Record<string, any> | undefined;
210
476
  outputs?: any[] | undefined;
211
477
  version?: number | undefined;
@@ -240,7 +506,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
240
506
  sortingKey: string;
241
507
  blockGroup?: string | undefined;
242
508
  content?: string | undefined;
509
+ contentHash?: string | undefined;
243
510
  executionCount?: number | undefined;
511
+ executionFinishedAt?: string | undefined;
512
+ executionStartedAt?: string | undefined;
244
513
  metadata?: Record<string, any> | undefined;
245
514
  outputs?: any[] | undefined;
246
515
  version?: number | undefined;
@@ -285,7 +554,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
285
554
  sortingKey: string;
286
555
  blockGroup?: string | undefined;
287
556
  content?: string | undefined;
557
+ contentHash?: string | undefined;
288
558
  executionCount?: number | undefined;
559
+ executionFinishedAt?: string | undefined;
560
+ executionStartedAt?: string | undefined;
289
561
  metadata?: Record<string, any> | undefined;
290
562
  outputs?: any[] | undefined;
291
563
  version?: number | undefined;
@@ -309,6 +581,33 @@ declare const deepnoteFileSchema: z.ZodObject<{
309
581
  sqlCacheMaxAge?: number | undefined;
310
582
  } | undefined;
311
583
  };
584
+ environment?: {
585
+ customImage?: string | undefined;
586
+ hash?: string | undefined;
587
+ packages?: Record<string, string> | undefined;
588
+ platform?: string | undefined;
589
+ python?: {
590
+ version?: string | undefined;
591
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
592
+ } | undefined;
593
+ } | undefined;
594
+ execution?: {
595
+ error?: {
596
+ message?: string | undefined;
597
+ name?: string | undefined;
598
+ traceback?: string[] | undefined;
599
+ } | undefined;
600
+ finishedAt?: string | undefined;
601
+ inputs?: Record<string, unknown> | undefined;
602
+ startedAt?: string | undefined;
603
+ summary?: {
604
+ blocksExecuted?: number | undefined;
605
+ blocksFailed?: number | undefined;
606
+ blocksSucceeded?: number | undefined;
607
+ totalDurationMs?: number | undefined;
608
+ } | undefined;
609
+ triggeredBy?: "user" | "schedule" | "api" | "ci" | undefined;
610
+ } | undefined;
312
611
  }, {
313
612
  metadata: {
314
613
  createdAt: string;
@@ -329,7 +628,10 @@ declare const deepnoteFileSchema: z.ZodObject<{
329
628
  sortingKey: string;
330
629
  blockGroup?: string | undefined;
331
630
  content?: string | undefined;
631
+ contentHash?: string | undefined;
332
632
  executionCount?: number | undefined;
633
+ executionFinishedAt?: string | undefined;
634
+ executionStartedAt?: string | undefined;
333
635
  metadata?: Record<string, any> | undefined;
334
636
  outputs?: any[] | undefined;
335
637
  version?: number | undefined;
@@ -353,6 +655,33 @@ declare const deepnoteFileSchema: z.ZodObject<{
353
655
  sqlCacheMaxAge?: number | undefined;
354
656
  } | undefined;
355
657
  };
658
+ environment?: {
659
+ customImage?: string | undefined;
660
+ hash?: string | undefined;
661
+ packages?: Record<string, string> | undefined;
662
+ platform?: string | undefined;
663
+ python?: {
664
+ version?: string | undefined;
665
+ environment?: "uv" | "conda" | "venv" | "poetry" | "system" | undefined;
666
+ } | undefined;
667
+ } | undefined;
668
+ execution?: {
669
+ error?: {
670
+ message?: string | undefined;
671
+ name?: string | undefined;
672
+ traceback?: string[] | undefined;
673
+ } | undefined;
674
+ finishedAt?: string | undefined;
675
+ inputs?: Record<string, unknown> | undefined;
676
+ startedAt?: string | undefined;
677
+ summary?: {
678
+ blocksExecuted?: number | undefined;
679
+ blocksFailed?: number | undefined;
680
+ blocksSucceeded?: number | undefined;
681
+ totalDurationMs?: number | undefined;
682
+ } | undefined;
683
+ triggeredBy?: "user" | "schedule" | "api" | "ci" | undefined;
684
+ } | undefined;
356
685
  }>;
357
686
  type DeepnoteFile = z.infer<typeof deepnoteFileSchema>;
358
687
  //#endregion
@@ -362,6 +691,24 @@ type DeepnoteFile = z.infer<typeof deepnoteFileSchema>;
362
691
  */
363
692
  declare function deserializeDeepnoteFile(yamlContent: string): DeepnoteFile;
364
693
  //#endregion
694
+ //#region src/deserialize-file/parse-yaml.d.ts
695
+ /**
696
+ * Validates UTF-8 encoding from raw bytes before decoding to string.
697
+ * This is the proper way to validate UTF-8 - check BEFORE decoding.
698
+ *
699
+ * @param bytes - Raw file bytes as Uint8Array
700
+ * @returns Decoded UTF-8 string without BOM
701
+ * @throws Error if BOM detected or invalid UTF-8 encoding
702
+ *
703
+ * @example
704
+ * ```typescript
705
+ * const bytes = await fs.readFile('file.deepnote') // Returns Buffer/Uint8Array
706
+ * const yamlContent = decodeUtf8NoBom(bytes)
707
+ * const parsed = parseYaml(yamlContent)
708
+ * ```
709
+ */
710
+ declare function decodeUtf8NoBom(bytes: Uint8Array): string;
711
+ //#endregion
365
712
  //#region src/markdown.d.ts
366
713
  declare function createMarkdown(block: DeepnoteBlock): string;
367
714
  declare function stripMarkdown(block: DeepnoteBlock): string;
@@ -378,4 +725,4 @@ interface ButtonExecutionContext {
378
725
  //#region src/python-code.d.ts
379
726
  declare function createPythonCode(block: DeepnoteBlock, executionContext?: ButtonExecutionContext): string;
380
727
  //#endregion
381
- export { type DeepnoteBlock, type DeepnoteFile, type TableState, createMarkdown, createPythonCode, deepnoteBlockSchema, deserializeDeepnoteFile, stripMarkdown };
728
+ export { type DeepnoteBlock, type DeepnoteFile, type Environment, type Execution, type ExecutionError, type ExecutionSummary, type TableState, createMarkdown, createPythonCode, decodeUtf8NoBom, deepnoteBlockSchema, deepnoteFileSchema, deserializeDeepnoteFile, environmentSchema, executionErrorSchema, executionSchema, executionSummarySchema, stripMarkdown };
package/dist/index.js CHANGED
@@ -1,12 +1,15 @@
1
1
  import { z } from "zod";
2
- import { parse } from "yaml";
2
+ import { parseDocument } from "yaml";
3
3
  import { dedent } from "ts-dedent";
4
4
 
5
5
  //#region src/deserialize-file/deepnote-file-schema.ts
6
6
  const deepnoteBlockSchema = z.object({
7
7
  blockGroup: z.string().optional(),
8
8
  content: z.string().optional(),
9
+ contentHash: z.string().regex(/^(md5|sha256):[a-f0-9]+$/i).optional(),
9
10
  executionCount: z.number().optional(),
11
+ executionFinishedAt: z.string().datetime().optional(),
12
+ executionStartedAt: z.string().datetime().optional(),
10
13
  id: z.string(),
11
14
  metadata: z.record(z.any()).optional(),
12
15
  outputs: z.array(z.any()).optional(),
@@ -14,7 +17,49 @@ const deepnoteBlockSchema = z.object({
14
17
  type: z.string(),
15
18
  version: z.number().optional()
16
19
  });
20
+ const environmentSchema = z.object({
21
+ customImage: z.string().optional(),
22
+ hash: z.string().optional(),
23
+ packages: z.record(z.string()).optional(),
24
+ platform: z.string().optional(),
25
+ python: z.object({
26
+ environment: z.enum([
27
+ "uv",
28
+ "conda",
29
+ "venv",
30
+ "poetry",
31
+ "system"
32
+ ]).optional(),
33
+ version: z.string().optional()
34
+ }).optional()
35
+ }).optional();
36
+ const executionSummarySchema = z.object({
37
+ blocksExecuted: z.number().int().nonnegative().optional(),
38
+ blocksFailed: z.number().int().nonnegative().optional(),
39
+ blocksSucceeded: z.number().int().nonnegative().optional(),
40
+ totalDurationMs: z.number().nonnegative().optional()
41
+ }).optional();
42
+ const executionErrorSchema = z.object({
43
+ message: z.string().optional(),
44
+ name: z.string().optional(),
45
+ traceback: z.array(z.string()).optional()
46
+ }).optional();
47
+ const executionSchema = z.object({
48
+ error: executionErrorSchema,
49
+ finishedAt: z.string().datetime().optional(),
50
+ inputs: z.record(z.unknown()).optional(),
51
+ startedAt: z.string().datetime().optional(),
52
+ summary: executionSummarySchema,
53
+ triggeredBy: z.enum([
54
+ "user",
55
+ "schedule",
56
+ "api",
57
+ "ci"
58
+ ]).optional()
59
+ }).optional();
17
60
  const deepnoteFileSchema = z.object({
61
+ environment: environmentSchema,
62
+ execution: executionSchema,
18
63
  metadata: z.object({
19
64
  checksum: z.string().optional(),
20
65
  createdAt: z.string(),
@@ -52,9 +97,88 @@ const deepnoteFileSchema = z.object({
52
97
 
53
98
  //#endregion
54
99
  //#region src/deserialize-file/parse-yaml.ts
100
+ /**
101
+ * Validates UTF-8 encoding from raw bytes before decoding to string.
102
+ * This is the proper way to validate UTF-8 - check BEFORE decoding.
103
+ *
104
+ * @param bytes - Raw file bytes as Uint8Array
105
+ * @returns Decoded UTF-8 string without BOM
106
+ * @throws Error if BOM detected or invalid UTF-8 encoding
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const bytes = await fs.readFile('file.deepnote') // Returns Buffer/Uint8Array
111
+ * const yamlContent = decodeUtf8NoBom(bytes)
112
+ * const parsed = parseYaml(yamlContent)
113
+ * ```
114
+ */
115
+ function decodeUtf8NoBom(bytes) {
116
+ if (bytes.length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) throw new Error("UTF-8 BOM detected in Deepnote file - files must be UTF-8 without BOM");
117
+ try {
118
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
119
+ } catch {
120
+ throw new Error("Invalid UTF-8 encoding detected in Deepnote file");
121
+ }
122
+ }
123
+ /**
124
+ * Validates that a string doesn't start with BOM.
125
+ * Note: This is a fallback when only a string is available.
126
+ * By the time we have a JS string, invalid UTF-8 has already been handled during decoding.
127
+ * For proper UTF-8 validation, use decodeUtf8NoBom() on raw bytes before decoding.
128
+ *
129
+ * @param yamlContent - Already-decoded YAML string
130
+ * @throws Error if BOM prefix detected
131
+ */
132
+ function validateNoBomPrefix(yamlContent) {
133
+ if (yamlContent.charCodeAt(0) === 65279) throw new Error("UTF-8 BOM detected in Deepnote file - files must be UTF-8 without BOM");
134
+ }
135
+ /**
136
+ * Validates that the YAML document doesn't contain prohibited features:
137
+ * - No anchors/aliases (&anchor, *alias)
138
+ * - No merge keys (<<)
139
+ * - No custom tags (!tag)
140
+ */
141
+ function validateYamlStructure(yamlContent) {
142
+ if (/(?:^|\n)\s*(?:-\s+|[\w-]+:\s*)&\w+/.test(yamlContent)) throw new Error("YAML anchors (&) are not allowed in Deepnote files");
143
+ if (/(?:^|\n)\s*(?:-\s+|[\w-]+:\s*)\*\w+/.test(yamlContent)) throw new Error("YAML aliases (*) are not allowed in Deepnote files");
144
+ if (/<<:/.test(yamlContent)) throw new Error("YAML merge keys (<<) are not allowed in Deepnote files");
145
+ const matches = yamlContent.match(/(?:^|\n)\s*(?:-\s+|[\w-]+:\s*)(![\w/-]+)/gm);
146
+ if (matches) {
147
+ const tags = matches.map((m) => m.match(/(![\w/-]+)/)?.[1]).filter(Boolean);
148
+ if (tags.length > 0) throw new Error(`YAML tags are not allowed in Deepnote files: ${tags.join(", ")}`);
149
+ }
150
+ }
151
+ /**
152
+ * Parse and validate YAML document in a single pass.
153
+ * Checks for duplicate keys and other parsing errors, then converts to JavaScript object.
154
+ */
155
+ function parseAndValidate(yamlContent) {
156
+ const doc = parseDocument(yamlContent, {
157
+ strict: true,
158
+ uniqueKeys: true,
159
+ version: "1.2"
160
+ });
161
+ if (doc.errors.length > 0) {
162
+ const duplicateKeyError = doc.errors.find((err) => err.message.includes("duplicate") || err.message.includes("key"));
163
+ if (duplicateKeyError) throw new Error(`Duplicate keys detected in Deepnote file: ${duplicateKeyError.message}`);
164
+ throw new Error(`YAML parsing error: ${doc.errors[0].message}`);
165
+ }
166
+ return doc.toJS();
167
+ }
168
+ /**
169
+ * Parse YAML content with strict validation rules:
170
+ * - YAML 1.2 only
171
+ * - UTF-8 only
172
+ * - No duplicate keys
173
+ * - No anchors/aliases/merge keys
174
+ * - No custom tags
175
+ * - Explicit typing enforced by schema
176
+ */
55
177
  function parseYaml(yamlContent) {
56
178
  try {
57
- return parse(yamlContent);
179
+ validateNoBomPrefix(yamlContent);
180
+ validateYamlStructure(yamlContent);
181
+ return parseAndValidate(yamlContent);
58
182
  } catch (error) {
59
183
  const message = error instanceof Error ? error.message : String(error);
60
184
  throw new Error(`Failed to parse Deepnote file: ${message}`);
@@ -565,4 +689,4 @@ function createPythonCode(block, executionContext) {
565
689
  }
566
690
 
567
691
  //#endregion
568
- export { createMarkdown, createPythonCode, deepnoteBlockSchema, deserializeDeepnoteFile, stripMarkdown };
692
+ export { createMarkdown, createPythonCode, decodeUtf8NoBom, deepnoteBlockSchema, deepnoteFileSchema, deserializeDeepnoteFile, environmentSchema, executionErrorSchema, executionSchema, executionSummarySchema, stripMarkdown };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepnote/blocks",
3
- "version": "1.3.5",
3
+ "version": "1.4.0",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "repository": {
@@ -31,6 +31,10 @@
31
31
  "yaml": "^2.8.1",
32
32
  "zod": "3.25.76"
33
33
  },
34
+ "engines": {
35
+ "node": ">=22.14.0",
36
+ "pnpm": ">=10.17.1"
37
+ },
34
38
  "publishConfig": {
35
39
  "access": "public",
36
40
  "registry": "https://registry.npmjs.org"