From 283bce2d718ae5d4302f863f2831b4eb4ea57f5b Mon Sep 17 00:00:00 2001 From: tomas Date: Sat, 7 Nov 2026 16:30:20 +0000 Subject: [PATCH 1/2] chore(deps): update @deepnote packages to their zod 4 releases Move every @deepnote/* dependency to its latest release: blocks 5.0.0, convert 5.0.0, database-integrations 2.0.0, runtime-core 0.8.0, cloud 0.3.0, cloud-sync 0.2.0 and cli 0.11.0. sql-language-server 3.0.1 is already the latest. These releases import the zod 4 API and declare zod ^4.6.7, so the lockfile resolves a single zod 4.6.7 and one copy of each @deepnote package, including under @deepnote/cli, local-runner and reactivity. All nested zod 3.25.76 copies are gone, so the GHSA-mrhx-c7xc-m3w5 exception in .nsprc and osv-scanner.toml no longer matches anything and is removed. Raise the root yaml override from 2.8.3 to 2.9.1: the @deepnote packages declare yaml ^2.9.0, and the old pin held them below that floor. Drop the stale version label from the database-integrations heading in specs/INTEGRATIONS_CREDENTIALS.md. --- .nsprc | 4 - osv-scanner.toml | 5 - package-lock.json | 642 ++++-------------------------- package.json | 16 +- specs/INTEGRATIONS_CREDENTIALS.md | 2 +- 5 files changed, 85 insertions(+), 584 deletions(-) diff --git a/.nsprc b/.nsprc index 497f8dfcd2..a59af647b2 100644 --- a/.nsprc +++ b/.nsprc @@ -11,10 +11,6 @@ "notes": "CVE-2026-118764: at optimizationLevel >= 2, minimatch's firstPhasePreProcess() splits every `**/..` portion of a pattern inside a fixpoint loop and secondPhasePreProcess() then compares every pair, so a short crafted pattern (14 repetitions of `**/../a/b` take about 4 s) exhausts CPU. Only the pattern triggers it, never the matched path, and only at optimizationLevel 2, which glob 9+ sets and minimatch's default (1) does not. Accepted risk: the fix exists only in minimatch 11.0.1+ (3.x, 5.x, 9.x and 10.x have no backport), no dependent in this tree except glob 14 admits 11, and 11.0 is a rewrite (no default export, makeRe and matchOne removed) that an override cannot force without breaking glob 7 and 10 matching, test-exclude 6 and eslint-plugin-import (they call minimatch as a function) and the default import in src/platform/interpreter/reservedNamedProvider.node.ts. The shipped copies do not contain the vulnerable preprocessing and are flagged by version range only: 3.1.5 (hoisted; bundled through reservedNamedProvider.node.ts's direct import, and prod-optional via @deepnote/sql-language-server -> sqlite3 -> node-gyp/cacache -> glob 7, install-time tooling) and 5.1.9 (vscode-languageclient 9.0.1). The shipped glob 14.0.3 runs its own inlined, fixed minimatch. The copies that contain the vulnerable code are dev-only and receive patterns only from this repository's own configuration: 9.0.9 under mocha 11.8.0 and rimraf 5.0.10 (via glob 10), and 10.2.5/10.2.6 under @vscode/vsce, @vscode/test-cli, c8, vscode-extension-tester and @actions/glob; in src/test/vscode-notebook-perf (mirrored in its osv-scanner.toml), 10.2.6 (hoisted) and 9.0.9 under @vscode/test-cli's mocha. The latest vscode-languageclient 11.0.0, mocha 12.0.3, @vscode/vsce 4.1.0, @vscode/test-cli 0.0.15 and test-exclude 8.0.0 still declare minimatch ^10, and eslint-plugin-import 2.32.0 declares ^3.1.2. Revisit at expiry for backports or dependents that admit minimatch >=11.0.1.", "expiry": "2026-11-15" }, - "GHSA-mrhx-c7xc-m3w5": { - "notes": "CVE-2023-54404: zod builds one issue per invalid element while validating an oversized collection, so a huge input can exhaust memory (denial of service). Accepted risk: no patched zod 3.x release exists (range <4.6.6), and every @deepnote/* dependency pins zod to exactly 3.25.76, so a lockfile bump cannot reach a fix and an override would force an untested major under them. The shipped bundles use this zod to validate local .deepnote project files before notebook execution, including in limited-support untrusted workspaces. A crafted file that the user opens can stall or exhaust the extension host or CLI, with no data exposure. The 4.6.6 fix also only short-circuits collections with a .max()/.length()/.size() bound, so moving to zod 4 alone would clear the scanner without changing this behavior.", - "expiry": "2026-11-10" - }, "GHSA-xcx6-jx8p-x3h5": { "notes": "CVE-2026-109461: @azure/msal-node's authorization code flow (acquireTokenByCode) may disclose request parameters to an attacker-controlled endpoint. Accepted risk: dev-only and not shipped (`npm ls @azure/msal-node --omit=dev` is empty; node_modules/** is excluded from the VSIX). One copy, @azure/msal-node@6.0.1, reached via @vscode/vsce (root and under vscode-extension-tester) -> @azure/identity@4.14.0. vsce uses @azure/identity only for `vsce publish` / `verify-pat --azure-credential`, through a chain of environment, Azure CLI and managed-identity credentials that never runs the authorization code flow; this repository only runs `vsce package`. The fix exists only on the 7.x line (first patched 7.1.0; 6.x has no backport), and the latest @azure/identity (4.14.0) still declares @azure/msal-node ^6.0.0, so a lockfile bump cannot reach it. Remove this entry and its osv-scanner.toml mirror once an @azure/identity release admits msal-node >=7.1.0.", "expiry": "2026-11-10" diff --git a/osv-scanner.toml b/osv-scanner.toml index 12c2779976..cb82b88d7c 100644 --- a/osv-scanner.toml +++ b/osv-scanner.toml @@ -16,11 +16,6 @@ id = "GHSA-g3hv-75wg-m249" ignoreUntil = 2026-11-15 reason = "CVE-2026-118764 (minimatch): crafted-pattern DoS; shipped copies lack the code, dev copies get repo patterns only; fix only in 11.x. See .nsprc." -[[IgnoredVulns]] -id = "GHSA-mrhx-c7xc-m3w5" -ignoreUntil = 2026-11-10 -reason = "CVE-2023-54404 (zod): local DoS only, via user-opened project files; no patched 3.x release. See .nsprc." - [[IgnoredVulns]] id = "GHSA-xcx6-jx8p-x3h5" ignoreUntil = 2026-11-10 diff --git a/package-lock.json b/package-lock.json index ac2de3458c..bdd00da597 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,13 +11,13 @@ "license": "MIT", "dependencies": { "@c4312/evt": "^0.1.1", - "@deepnote/blocks": "^4.8.1", - "@deepnote/cli": "0.8.0", - "@deepnote/cloud": "0.2.1", - "@deepnote/cloud-sync": "0.1.0", - "@deepnote/convert": "^4.0.3", - "@deepnote/database-integrations": "^1.7.1", - "@deepnote/runtime-core": "^0.7.0", + "@deepnote/blocks": "^5.0.0", + "@deepnote/cli": "0.11.0", + "@deepnote/cloud": "0.3.0", + "@deepnote/cloud-sync": "0.2.0", + "@deepnote/convert": "^5.0.0", + "@deepnote/database-integrations": "^2.0.0", + "@deepnote/runtime-core": "^0.8.0", "@deepnote/sql-language-server": "^3.0.1", "@enonic/fnv-plus": "^1.3.0", "@jupyter-widgets/base": "^6.0.8", @@ -2006,237 +2006,52 @@ } }, "node_modules/@deepnote/blocks": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/@deepnote/blocks/-/blocks-4.8.1.tgz", - "integrity": "sha512-RWNTvZ4bar31IQ/tSqF2zUwJUCd+KJneZeyHrcixv559t6+WVQweziJUlw2etguTkOxhn7Zmj+MO+/OJkVYk/Q==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@deepnote/blocks/-/blocks-5.0.0.tgz", + "integrity": "sha512-oDv9Lcbh1gvMuONqdWf7Ya5c1HAfiWm59mi4oyqQoW4SKrDvHVFVNcal41T1ddrF96gl6KfmxDR0ZGuiT61Azg==", "license": "Apache-2.0", "dependencies": { "ts-dedent": "^2.3.0", "yaml": "^2.9.0", - "zod": "3.25.76" + "zod": "^4.6.7" }, "engines": { "node": ">=22.14.0", "pnpm": ">=10.17.1" } }, - "node_modules/@deepnote/blocks/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@deepnote/cli": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@deepnote/cli/-/cli-0.8.0.tgz", - "integrity": "sha512-7H57rsaXgMqKqdsAGGB/FvPYX8Y9H5lF2UtJip0ph3FxOiyCiSrQkr3/WlEGvxceYyYEoBqvpDkhdh4JzEmxNw==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@deepnote/cli/-/cli-0.11.0.tgz", + "integrity": "sha512-0UcthXpzHfvi7sgAbg1zG16Y3yxhAl8GdsygBjwSHv8t9AbB0ADSHW3dHKEqIcPr/g5Co08M3g5/ludgx85n5g==", "license": "Apache-2.0", "dependencies": { - "@deepnote/blocks": "4.7.0", - "@deepnote/cloud": "0.1.0", - "@deepnote/convert": "4.0.1", - "@deepnote/database-integrations": "1.6.0", - "@deepnote/local-runner": "0.1.0", - "@deepnote/reactivity": "1.2.2", - "@deepnote/runtime-core": "0.5.0", + "@deepnote/blocks": "5.0.0", + "@deepnote/cloud": "0.3.0", + "@deepnote/cloud-sync": "0.2.0", + "@deepnote/convert": "5.0.0", + "@deepnote/database-integrations": "2.0.0", + "@deepnote/local-runner": "0.4.0", + "@deepnote/reactivity": "2.0.0", + "@deepnote/runtime-core": "0.8.0", "@inquirer/prompts": "^8.2.1", - "@shikijs/cli": "^4.0.0", - "@toon-format/toon": "^2.1.0", - "chalk": "^5.4.1", - "commander": "^14.0.2", + "@shikijs/cli": "^4.4.3", + "@toon-format/toon": "^2.3.1", + "chalk": "^5.6.2", + "commander": "^14.0.3", "diff": "^8.0.3", - "dotenv": "^17.2.3", + "dotenv": "^17.4.2", "marked": "^15.0.12", "marked-terminal": "^7.3.0", - "ora": "^9.0.0", + "ora": "^9.4.1", "wrap-ansi": "^9.0.2", - "yaml": "^2.8.3", - "zod": "3.25.76" + "yaml": "^2.9.0", + "zod": "^4.6.7" }, "bin": { "deepnote": "dist/bin.js" } }, - "node_modules/@deepnote/cli/node_modules/@ai-sdk/gateway": { - "version": "3.0.212", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-3.0.212.tgz", - "integrity": "sha512-iTJSim/6LuSxiG3RF0FWU7bEQ8dF6EplcWci5Km4Vc8SUcR8vxFuC7T/pfCutW00pI/3WYsHm8RCQ5141/Xykw==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.18", - "@ai-sdk/provider-utils": "4.0.57", - "@vercel/oidc": "3.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@deepnote/cli/node_modules/@ai-sdk/mcp": { - "version": "1.0.93", - "resolved": "https://registry.npmjs.org/@ai-sdk/mcp/-/mcp-1.0.93.tgz", - "integrity": "sha512-d+PYwDtcpQ23lRu08LijaNN1KzuJ4nTloHjPl43xdqs10uUmLNxTmyOq7oLSBkdnUgSEzv8y2vuAyBUlNfZGpg==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.18", - "@ai-sdk/provider-utils": "4.0.57", - "cross-spawn": "^7.0.6", - "pkce-challenge": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@deepnote/cli/node_modules/@ai-sdk/openai": { - "version": "3.0.126", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.126.tgz", - "integrity": "sha512-IHOe6MQgrC22WDUA+rQcfK/H3rUmtgHFRYh/BsnpHzXPyVQyvhymKh5r7fgxQsJ8vVyp+esPpQ7/4fr8hr6Iuw==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.18", - "@ai-sdk/provider-utils": "4.0.57" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@deepnote/cli/node_modules/@ai-sdk/provider": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.18.tgz", - "integrity": "sha512-IpefF5ssZVOZCD4Wu7zxt6PwZoGT2HLVjxpJ10Bjx6EAmRfLYJPjyrfxb0RYDlwvXnDb9nkQ7Do/oJVDuRFzew==", - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@deepnote/cli/node_modules/@ai-sdk/provider-utils": { - "version": "4.0.57", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.57.tgz", - "integrity": "sha512-89a7sPvZqXsP3PigcAQLWntu/duIVEcN/RyChAB+j6p3C7NwW767sVttvL8r0+BetTg63uAXQoYHCZZueQKBSw==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.18", - "@standard-schema/spec": "^1.1.0", - "eventsource-parser": "^3.0.8", - "undici": "^6.28.0" - }, - "engines": { - "node": ">=18.17" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@deepnote/cli/node_modules/@deepnote/blocks": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@deepnote/blocks/-/blocks-4.7.0.tgz", - "integrity": "sha512-GW5jIpO2Sr7R2LaokuF8js60FHw4DLk1e4bq6BnMs2QkxGr2QF6JbRONl82V7zEV3iJQesXX+mFuTMWp9jCxPA==", - "license": "Apache-2.0", - "dependencies": { - "ts-dedent": "^2.2.0", - "yaml": "^2.8.3", - "zod": "3.25.76" - }, - "engines": { - "node": ">=22.14.0", - "pnpm": ">=10.17.1" - } - }, - "node_modules/@deepnote/cli/node_modules/@deepnote/cloud": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@deepnote/cloud/-/cloud-0.1.0.tgz", - "integrity": "sha512-H71/mTymgYidKKPdEYKsyo4yW4UWWe1x+l7r7WIwxWwMAz9HbR5UZQE5xeKRb2GWDT5ohdAOdQ5a8LtJiMnruw==", - "license": "Apache-2.0", - "dependencies": { - "@deepnote/database-integrations": "1.6.0", - "fflate": "^0.8.2", - "zod": "3.25.76" - }, - "engines": { - "node": ">=22.14.0", - "pnpm": ">=10.17.1" - } - }, - "node_modules/@deepnote/cli/node_modules/@deepnote/convert": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@deepnote/convert/-/convert-4.0.1.tgz", - "integrity": "sha512-0Kb3SHZyU1rxWb0ynVkxzLEBd3BYkgShYTTFdqMm9rOXF2Bxn5yklxZBMb9SyA4yHqLx9sFv9c/+/X2SLJYDgA==", - "license": "Apache-2.0", - "dependencies": { - "@deepnote/blocks": "4.7.0", - "chalk": "^5.6.2", - "cleye": "^2.0.0", - "ora": "^9.0.0", - "yaml": "^2.8.3" - }, - "bin": { - "deepnote-convert": "dist/bin.js" - }, - "engines": { - "node": ">=22.14.0", - "pnpm": ">=10.17.1" - } - }, - "node_modules/@deepnote/cli/node_modules/@deepnote/database-integrations": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@deepnote/database-integrations/-/database-integrations-1.6.0.tgz", - "integrity": "sha512-Xy8Lbo10KNUL6PDW6qXMF0opyKgmbZfV3HKXVPZrBl7IsZnJmTiHPPNd5neGQ3coefsjcs5Kl17o9iPEX2kw2Q==", - "license": "Apache-2.0", - "dependencies": { - "@deepnote/blocks": "4.7.0", - "yaml": "^2.8.3", - "zod": "3.25.76" - } - }, - "node_modules/@deepnote/cli/node_modules/@deepnote/runtime-core": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@deepnote/runtime-core/-/runtime-core-0.5.0.tgz", - "integrity": "sha512-+0Dbs5IhSsRFLg5XZqmr1gKo0GPeMwKNxezhAP6MgYmVECvxb+kHRoI5i4aazRsni/hk8i5AqipyYKYu4TT3Og==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/mcp": "^1.0.25", - "@ai-sdk/openai": "^3.0.0", - "@deepnote/blocks": "4.7.0", - "@jupyterlab/nbformat": "^4.3.2", - "@jupyterlab/services": "^7.3.2", - "ai": "^6.0.0", - "tcp-port-used": "^1.0.2", - "ws": "^8.20.1", - "zod": "3.25.76" - } - }, - "node_modules/@deepnote/cli/node_modules/ai": { - "version": "6.0.304", - "resolved": "https://registry.npmjs.org/ai/-/ai-6.0.304.tgz", - "integrity": "sha512-xhKVjs+pEGLVZ8mIbkv1UTThsNYnlr7lQRkroKFZDPYqhlPyUHGE+ICQsbAX4wyP0ELjej5/Bhd3a2owIf+UhQ==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/gateway": "3.0.212", - "@ai-sdk/provider": "3.0.18", - "@ai-sdk/provider-utils": "4.0.57", - "@opentelemetry/api": "^1.9.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, "node_modules/@deepnote/cli/node_modules/ansi-regex": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", @@ -2334,45 +2149,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@deepnote/cli/node_modules/ws": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", - "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@deepnote/cli/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@deepnote/cloud": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@deepnote/cloud/-/cloud-0.2.1.tgz", - "integrity": "sha512-o01TEAQ2LxA4/ulWK+9onsqLdd6d9EwLIlD3oMeSLAqH2b+eYSmnbsLWgnFQ5fUpYTpPtinlVlui5pu/LbF3lA==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@deepnote/cloud/-/cloud-0.3.0.tgz", + "integrity": "sha512-Ugy8eI62jJInxGLvf2+kcGttyrwuRIW3HdxwEn+RGl1+mmTM5Bu2dw09ZJzfg2GMuKPw98OMszyxfTu8NyD1zg==", "license": "Apache-2.0", "dependencies": { - "@deepnote/database-integrations": "1.7.1", + "@deepnote/database-integrations": "2.0.0", "fflate": "^0.8.3", - "zod": "3.25.76" + "zod": "^4.6.7" }, "engines": { "node": ">=22.14.0", @@ -2380,46 +2165,28 @@ } }, "node_modules/@deepnote/cloud-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@deepnote/cloud-sync/-/cloud-sync-0.1.0.tgz", - "integrity": "sha512-GrN8a8xpnEeM2d8E7y5g4FVWhvZWMhw3JEEta8HW+rHjALrWAPmLp88CvQD1JgSvRn0sL4v6r36Yt8MVDP92AQ==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@deepnote/cloud-sync/-/cloud-sync-0.2.0.tgz", + "integrity": "sha512-QQ9w9yVYOtxcbgRlaCCrMNi4EZc80hD9ad0SFcVhgmFo0P3Ysy8NtEeOSQyyBAjDDL2O85sBxSpnPSoZHmXfsg==", "license": "Apache-2.0", "dependencies": { - "@deepnote/blocks": "4.8.1", - "@deepnote/cloud": "0.2.1", - "@deepnote/database-integrations": "1.7.1", - "zod": "3.25.76" + "@deepnote/blocks": "5.0.0", + "@deepnote/cloud": "0.3.0", + "@deepnote/database-integrations": "2.0.0", + "zod": "^4.6.7" }, "engines": { "node": ">=22.14.0", "pnpm": ">=10.17.1" } }, - "node_modules/@deepnote/cloud-sync/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/@deepnote/cloud/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@deepnote/convert": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@deepnote/convert/-/convert-4.0.3.tgz", - "integrity": "sha512-uEZxxrUAL4vNrlHdwSV7hOUgdcTMFHJqv1xWmrb8e2QtTpxL1YTACvAkT0ptRxpQN0Km6vagzLMqkNuiJRo21A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@deepnote/convert/-/convert-5.0.0.tgz", + "integrity": "sha512-mW3ZKyKPWaoAfDi+DvzbGMN9ItasDuzcaL7J4AA4zXNX4weqQItNLDNX1T0UNaqye8RCelMxXHrNeBFaQQtLRA==", "license": "Apache-2.0", "dependencies": { - "@deepnote/blocks": "4.8.1", + "@deepnote/blocks": "5.0.0", "chalk": "^5.6.2", "cleye": "^2.6.0", "ora": "^9.4.1", @@ -2434,301 +2201,61 @@ } }, "node_modules/@deepnote/database-integrations": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@deepnote/database-integrations/-/database-integrations-1.7.1.tgz", - "integrity": "sha512-GbW+wibPjAd2/1fMOu4PlPdjfCu+aOoUWY6m6aHwCXmexutCoPN5H0U7RcxJB2qiafkEMUmtDwdSvxTqkbvFkA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@deepnote/database-integrations/-/database-integrations-2.0.0.tgz", + "integrity": "sha512-WUs4z7uOdwuS6NrK6oIyFfncQy4NjTpPQDAD805aMfcZumxdeh+yZrbuHfSlG7yi8Pr1F4biloiEzZb+O+6dAQ==", "license": "Apache-2.0", "dependencies": { - "@deepnote/blocks": "4.8.1", + "@deepnote/blocks": "5.0.0", "yaml": "^2.9.0", - "zod": "3.25.76" - } - }, - "node_modules/@deepnote/database-integrations/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "zod": "^4.6.7" } }, "node_modules/@deepnote/local-runner": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@deepnote/local-runner/-/local-runner-0.1.0.tgz", - "integrity": "sha512-5EJCy0KrAOijdJa8AJWew5W/QP1XLEZWN39mTSKHBPsIbeta55EskEvp3KMbQakgSV9hjnzWY5MihQc5y9k6Pw==", - "license": "Apache-2.0", - "dependencies": { - "@deepnote/blocks": "4.7.0", - "@deepnote/cloud": "0.1.0", - "@deepnote/convert": "4.0.1", - "@deepnote/runtime-core": "0.5.0", - "@jupyterlab/nbformat": "^4.3.2" - }, - "engines": { - "node": ">=22.14.0", - "pnpm": ">=10.17.1" - } - }, - "node_modules/@deepnote/local-runner/node_modules/@ai-sdk/gateway": { - "version": "3.0.212", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-3.0.212.tgz", - "integrity": "sha512-iTJSim/6LuSxiG3RF0FWU7bEQ8dF6EplcWci5Km4Vc8SUcR8vxFuC7T/pfCutW00pI/3WYsHm8RCQ5141/Xykw==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.18", - "@ai-sdk/provider-utils": "4.0.57", - "@vercel/oidc": "3.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@deepnote/local-runner/node_modules/@ai-sdk/mcp": { - "version": "1.0.93", - "resolved": "https://registry.npmjs.org/@ai-sdk/mcp/-/mcp-1.0.93.tgz", - "integrity": "sha512-d+PYwDtcpQ23lRu08LijaNN1KzuJ4nTloHjPl43xdqs10uUmLNxTmyOq7oLSBkdnUgSEzv8y2vuAyBUlNfZGpg==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.18", - "@ai-sdk/provider-utils": "4.0.57", - "cross-spawn": "^7.0.6", - "pkce-challenge": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@deepnote/local-runner/node_modules/@ai-sdk/openai": { - "version": "3.0.126", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.126.tgz", - "integrity": "sha512-IHOe6MQgrC22WDUA+rQcfK/H3rUmtgHFRYh/BsnpHzXPyVQyvhymKh5r7fgxQsJ8vVyp+esPpQ7/4fr8hr6Iuw==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.18", - "@ai-sdk/provider-utils": "4.0.57" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@deepnote/local-runner/node_modules/@ai-sdk/provider": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.18.tgz", - "integrity": "sha512-IpefF5ssZVOZCD4Wu7zxt6PwZoGT2HLVjxpJ10Bjx6EAmRfLYJPjyrfxb0RYDlwvXnDb9nkQ7Do/oJVDuRFzew==", - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@deepnote/local-runner/node_modules/@ai-sdk/provider-utils": { - "version": "4.0.57", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.57.tgz", - "integrity": "sha512-89a7sPvZqXsP3PigcAQLWntu/duIVEcN/RyChAB+j6p3C7NwW767sVttvL8r0+BetTg63uAXQoYHCZZueQKBSw==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.18", - "@standard-schema/spec": "^1.1.0", - "eventsource-parser": "^3.0.8", - "undici": "^6.28.0" - }, - "engines": { - "node": ">=18.17" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@deepnote/local-runner/node_modules/@deepnote/blocks": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@deepnote/blocks/-/blocks-4.7.0.tgz", - "integrity": "sha512-GW5jIpO2Sr7R2LaokuF8js60FHw4DLk1e4bq6BnMs2QkxGr2QF6JbRONl82V7zEV3iJQesXX+mFuTMWp9jCxPA==", - "license": "Apache-2.0", - "dependencies": { - "ts-dedent": "^2.2.0", - "yaml": "^2.8.3", - "zod": "3.25.76" - }, - "engines": { - "node": ">=22.14.0", - "pnpm": ">=10.17.1" - } - }, - "node_modules/@deepnote/local-runner/node_modules/@deepnote/cloud": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@deepnote/cloud/-/cloud-0.1.0.tgz", - "integrity": "sha512-H71/mTymgYidKKPdEYKsyo4yW4UWWe1x+l7r7WIwxWwMAz9HbR5UZQE5xeKRb2GWDT5ohdAOdQ5a8LtJiMnruw==", - "license": "Apache-2.0", - "dependencies": { - "@deepnote/database-integrations": "1.6.0", - "fflate": "^0.8.2", - "zod": "3.25.76" - }, - "engines": { - "node": ">=22.14.0", - "pnpm": ">=10.17.1" - } - }, - "node_modules/@deepnote/local-runner/node_modules/@deepnote/convert": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@deepnote/convert/-/convert-4.0.1.tgz", - "integrity": "sha512-0Kb3SHZyU1rxWb0ynVkxzLEBd3BYkgShYTTFdqMm9rOXF2Bxn5yklxZBMb9SyA4yHqLx9sFv9c/+/X2SLJYDgA==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@deepnote/local-runner/-/local-runner-0.4.0.tgz", + "integrity": "sha512-IOLoS/DZR3GmgtQblP0iahzEbtjVa0F4yCb+YQP1N7jaEUA9ll5fji9BfryaIYDDTQ4VBDvppmRW5iWWYMHy9A==", "license": "Apache-2.0", "dependencies": { - "@deepnote/blocks": "4.7.0", - "chalk": "^5.6.2", - "cleye": "^2.0.0", - "ora": "^9.0.0", - "yaml": "^2.8.3" - }, - "bin": { - "deepnote-convert": "dist/bin.js" + "@deepnote/blocks": "5.0.0", + "@deepnote/cloud": "0.3.0", + "@deepnote/convert": "5.0.0", + "@deepnote/runtime-core": "0.8.0", + "@jupyterlab/nbformat": "^4.6.3" }, "engines": { "node": ">=22.14.0", "pnpm": ">=10.17.1" } }, - "node_modules/@deepnote/local-runner/node_modules/@deepnote/database-integrations": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@deepnote/database-integrations/-/database-integrations-1.6.0.tgz", - "integrity": "sha512-Xy8Lbo10KNUL6PDW6qXMF0opyKgmbZfV3HKXVPZrBl7IsZnJmTiHPPNd5neGQ3coefsjcs5Kl17o9iPEX2kw2Q==", - "license": "Apache-2.0", - "dependencies": { - "@deepnote/blocks": "4.7.0", - "yaml": "^2.8.3", - "zod": "3.25.76" - } - }, - "node_modules/@deepnote/local-runner/node_modules/@deepnote/runtime-core": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@deepnote/runtime-core/-/runtime-core-0.5.0.tgz", - "integrity": "sha512-+0Dbs5IhSsRFLg5XZqmr1gKo0GPeMwKNxezhAP6MgYmVECvxb+kHRoI5i4aazRsni/hk8i5AqipyYKYu4TT3Og==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/mcp": "^1.0.25", - "@ai-sdk/openai": "^3.0.0", - "@deepnote/blocks": "4.7.0", - "@jupyterlab/nbformat": "^4.3.2", - "@jupyterlab/services": "^7.3.2", - "ai": "^6.0.0", - "tcp-port-used": "^1.0.2", - "ws": "^8.20.1", - "zod": "3.25.76" - } - }, - "node_modules/@deepnote/local-runner/node_modules/ai": { - "version": "6.0.304", - "resolved": "https://registry.npmjs.org/ai/-/ai-6.0.304.tgz", - "integrity": "sha512-xhKVjs+pEGLVZ8mIbkv1UTThsNYnlr7lQRkroKFZDPYqhlPyUHGE+ICQsbAX4wyP0ELjej5/Bhd3a2owIf+UhQ==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/gateway": "3.0.212", - "@ai-sdk/provider": "3.0.18", - "@ai-sdk/provider-utils": "4.0.57", - "@opentelemetry/api": "^1.9.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@deepnote/local-runner/node_modules/ws": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", - "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@deepnote/local-runner/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@deepnote/reactivity": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@deepnote/reactivity/-/reactivity-1.2.2.tgz", - "integrity": "sha512-Ldp+0MHHZ4tUi4jbzJ3Ak1mv2sT4SO5HoDsTTzvqIwZYuqHKmNYrksYHqOB4qcv39uOZE2rXiLvIj6yGGHFfxQ==", - "license": "Apache-2.0", - "dependencies": { - "@deepnote/blocks": "4.7.0", - "zod": "3.25.76" - } - }, - "node_modules/@deepnote/reactivity/node_modules/@deepnote/blocks": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@deepnote/blocks/-/blocks-4.7.0.tgz", - "integrity": "sha512-GW5jIpO2Sr7R2LaokuF8js60FHw4DLk1e4bq6BnMs2QkxGr2QF6JbRONl82V7zEV3iJQesXX+mFuTMWp9jCxPA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@deepnote/reactivity/-/reactivity-2.0.0.tgz", + "integrity": "sha512-0oKD1mvRaXsVflFwP/3Mtjx9GSVPM8eJRjkpkrBhb0Wz6BuCXF64MFfGDI3n2iIkRrtxEnSPJLhiR7+ga8JFqg==", "license": "Apache-2.0", "dependencies": { - "ts-dedent": "^2.2.0", - "yaml": "^2.8.3", - "zod": "3.25.76" - }, - "engines": { - "node": ">=22.14.0", - "pnpm": ">=10.17.1" - } - }, - "node_modules/@deepnote/reactivity/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "@deepnote/blocks": "5.0.0", + "zod": "^4.6.7" } }, "node_modules/@deepnote/runtime-core": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@deepnote/runtime-core/-/runtime-core-0.7.0.tgz", - "integrity": "sha512-/3kPiDBNUaIsbuh3DWT0SI+8z7UPtaN9PRxEz21Qn8pQBIJD/UzMQDX+RSsXCGZTdSgi7EbrzzVFKr7Kv8/z2Q==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@deepnote/runtime-core/-/runtime-core-0.8.0.tgz", + "integrity": "sha512-H1Cjels9+DpSqL0yp42lpnt5Kn9xWVE8pqoKEquIdbYvJRCnhEYq3TN+a7SVqx5NsUT4qeB75DHFkoeFSEz/9Q==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/anthropic": "^4.0.76", "@ai-sdk/mcp": "^2.0.71", "@ai-sdk/openai": "^4.0.89", "@ai-sdk/openai-compatible": "^3.0.66", - "@deepnote/blocks": "4.8.1", + "@deepnote/blocks": "5.0.0", "@jupyterlab/nbformat": "^4.6.3", "@jupyterlab/services": "^7.6.3", "@lumino/signaling": "^2.1.6", "ai": "^7.0.133", "tcp-port-used": "^1.0.3", "ws": "^8.21.3", - "zod": "3.25.76" + "zod": "^4.6.7" }, "engines": { "node": ">=22.14.0" @@ -2755,15 +2282,6 @@ } } }, - "node_modules/@deepnote/runtime-core/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/@deepnote/sql-language-server": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@deepnote/sql-language-server/-/sql-language-server-3.0.1.tgz", @@ -7219,15 +6737,6 @@ "@octokit/openapi-types": "^27.0.0" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", - "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/@oxlint-tsgolint/darwin-arm64": { "version": "7.0.2004", "resolved": "https://registry.npmjs.org/@oxlint-tsgolint/darwin-arm64/-/darwin-arm64-7.0.2004.tgz", @@ -30494,6 +30003,7 @@ "version": "6.29.1", "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.1.tgz", "integrity": "sha512-EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18.17" @@ -33012,9 +32522,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -33196,9 +32706,9 @@ } }, "node_modules/zod": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.6.tgz", - "integrity": "sha512-4UmzQUF5fxvfcYHlbjBmejU+w+hbDnWFx4aS8kBx7nPBHcO2SqPbpAG8snbdg+Xse4pRefR98hgjFgIMCI/UhA==", + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 5d44bd247e..f4c988e40b 100644 --- a/package.json +++ b/package.json @@ -2735,13 +2735,13 @@ }, "dependencies": { "@c4312/evt": "^0.1.1", - "@deepnote/blocks": "^4.8.1", - "@deepnote/cli": "0.8.0", - "@deepnote/cloud": "0.2.1", - "@deepnote/cloud-sync": "0.1.0", - "@deepnote/convert": "^4.0.3", - "@deepnote/database-integrations": "^1.7.1", - "@deepnote/runtime-core": "^0.7.0", + "@deepnote/blocks": "^5.0.0", + "@deepnote/cli": "0.11.0", + "@deepnote/cloud": "0.3.0", + "@deepnote/cloud-sync": "0.2.0", + "@deepnote/convert": "^5.0.0", + "@deepnote/database-integrations": "^2.0.0", + "@deepnote/runtime-core": "^0.8.0", "@deepnote/sql-language-server": "^3.0.1", "@enonic/fnv-plus": "^1.3.0", "@jupyter-widgets/base": "^6.0.8", @@ -3014,7 +3014,7 @@ "bn.js@<4.12.3": "4.12.3", "serialize-javascript@<7.1.3": "7.1.3", "flatted@<3.4.2": "3.4.2", - "yaml": "2.8.3", + "yaml": "2.9.1", "ip-address": "10.7.4", "mermaid": "11.16.1", "postcss": "$postcss", diff --git a/specs/INTEGRATIONS_CREDENTIALS.md b/specs/INTEGRATIONS_CREDENTIALS.md index 0693f22c8e..a9e59cda50 100644 --- a/specs/INTEGRATIONS_CREDENTIALS.md +++ b/specs/INTEGRATIONS_CREDENTIALS.md @@ -15,7 +15,7 @@ The integrations system enables Deepnote notebooks to connect to external data s ### External Dependencies -#### **@deepnote/database-integrations Package** (v1.1.0) +#### **@deepnote/database-integrations Package** The system uses the `@deepnote/database-integrations` package as the source of truth for: From 1c9117292e4b650c01167ec8516d4f8a628a83e2 Mon Sep 17 00:00:00 2001 From: tomas Date: Sat, 7 Nov 2026 17:25:54 +0000 Subject: [PATCH 2/2] fix(integrations): generate federated BigQuery SQL code with @deepnote/blocks The federated BigQuery path built its Python from hand-copied @deepnote/blocks helpers. Their escapePythonString only escaped backslash, single quote and LF, so a query containing a carriage return (CRLF line endings) produced an unterminated string literal and the cell failed with a SyntaxError. Upstream has escaped CR and NUL since 4.8.1, and 5.0.0 also escapes tabs, other control characters and U+2028/2029. Call createPythonCodeForSqlBlockWithConnectionJson, which @deepnote/blocks has exported since 4.8.1 and which produces the same code otherwise (byte-identical for queries without control characters). This also validates deepnote_return_variable_type against its allowlist instead of interpolating it into the generated Python, matching the non-federated createPythonCode path. Delete vendoredBlocksHelpers.ts with its tests and the ts-dedent dependency that only it used. --- package-lock.json | 1 - package.json | 1 - ...federatedAuthSqlBlockCodeGenerator.node.ts | 29 +--- ...uthSqlBlockCodeGenerator.node.unit.test.ts | 10 ++ .../federatedAuth/federatedAuthTestHelpers.ts | 2 +- .../federatedAuth/vendoredBlocksHelpers.ts | 103 ------------- .../vendoredBlocksHelpers.unit.test.ts | 145 ------------------ 7 files changed, 13 insertions(+), 278 deletions(-) delete mode 100644 src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.ts delete mode 100644 src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.unit.test.ts diff --git a/package-lock.json b/package-lock.json index bdd00da597..e8b41382b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -95,7 +95,6 @@ "tailwind-merge": "^3.3.1", "tcp-port-used": "^1.0.1", "tmp": "^0.2.7", - "ts-dedent": "^2.2.0", "url-parse": "^1.5.10", "uuid": "^13.0.2", "vega": "^6.2.0", diff --git a/package.json b/package.json index f4c988e40b..ebc93ba8b6 100644 --- a/package.json +++ b/package.json @@ -2819,7 +2819,6 @@ "tailwind-merge": "^3.3.1", "tcp-port-used": "^1.0.1", "tmp": "^0.2.7", - "ts-dedent": "^2.2.0", "url-parse": "^1.5.10", "uuid": "^13.0.2", "vega": "^6.2.0", diff --git a/src/notebooks/deepnote/integrations/federatedAuth/federatedAuthSqlBlockCodeGenerator.node.ts b/src/notebooks/deepnote/integrations/federatedAuth/federatedAuthSqlBlockCodeGenerator.node.ts index 2a868c57ba..393e7f4d0e 100644 --- a/src/notebooks/deepnote/integrations/federatedAuth/federatedAuthSqlBlockCodeGenerator.node.ts +++ b/src/notebooks/deepnote/integrations/federatedAuth/federatedAuthSqlBlockCodeGenerator.node.ts @@ -1,4 +1,4 @@ -import type { DeepnoteBlock } from '@deepnote/blocks'; +import { createPythonCodeForSqlBlockWithConnectionJson, type DeepnoteBlock } from '@deepnote/blocks'; import { BigQueryAuthMethods } from '@deepnote/database-integrations'; import { inject, injectable } from 'inversify'; import { Uri } from 'vscode'; @@ -11,13 +11,6 @@ import { computeMetadataFingerprint } from './federatedAuthTokenStorage.node'; import { GOOGLE_TOKEN_URL } from './googleOAuthProvider.node'; -import { - createDataFrameConfig, - executeSqlQueryWithConnectionJson, - sanitizePythonVariableName, - SqlCacheMode, - SqlCellVariableType -} from './vendoredBlocksHelpers'; import { FederatedAuthTokenEntry, IFederatedAuthSqlBlockCodeGenerator, @@ -117,24 +110,6 @@ export class FederatedAuthSqlBlockCodeGenerator implements IFederatedAuthSqlBloc param_style: 'pyformat' }); - // Mirror upstream `createPythonCodeForSqlBlock`'s metadata reads. - const query = sqlBlock.content ?? ''; - const rawVariableName = sqlBlock.metadata?.deepnote_variable_name; - const pythonVariableName = - rawVariableName !== undefined ? (sanitizePythonVariableName(rawVariableName) ?? 'input_1') : undefined; - const returnVariableType: SqlCellVariableType = sqlBlock.metadata?.deepnote_return_variable_type ?? 'dataframe'; - const sqlCacheMode: SqlCacheMode = 'cache_disabled'; - - const dataFrameConfig = createDataFrameConfig(sqlBlock); - const executeSqlCall = executeSqlQueryWithConnectionJson({ - query, - auditComment: '', - connectionJson, - pythonVariableName, - sqlCacheMode, - returnVariableType - }); - - return `${dataFrameConfig}\n\n${executeSqlCall}`; + return createPythonCodeForSqlBlockWithConnectionJson(sqlBlock, { connectionJson }); } } diff --git a/src/notebooks/deepnote/integrations/federatedAuth/federatedAuthSqlBlockCodeGenerator.node.unit.test.ts b/src/notebooks/deepnote/integrations/federatedAuth/federatedAuthSqlBlockCodeGenerator.node.unit.test.ts index 472da32257..c0d264144f 100644 --- a/src/notebooks/deepnote/integrations/federatedAuth/federatedAuthSqlBlockCodeGenerator.node.unit.test.ts +++ b/src/notebooks/deepnote/integrations/federatedAuth/federatedAuthSqlBlockCodeGenerator.node.unit.test.ts @@ -385,4 +385,14 @@ suite('FederatedAuthSqlBlockCodeGenerator', () => { // Match upstream's shape: `my_df = _dntk.execute_sql_with_connection_json(...)` followed by `my_df` on the next line. assert.include(result, 'my_df = _dntk.execute_sql_with_connection_json('); }); + + test('escapes the carriage return of a CRLF query so the query stays one Python string literal', async () => { + setupValidFederatedIntegration(); + const result = await generator.generate(buildSqlBlock({ content: 'SELECT 1\r\nFROM t' }), NOTEBOOK_URI); + if (typeof result !== 'string') { + throw new Error(`expected a string result, got ${typeof result}`); + } + + assert.include(result, "'SELECT 1\\r\\nFROM t'"); + }); }); diff --git a/src/notebooks/deepnote/integrations/federatedAuth/federatedAuthTestHelpers.ts b/src/notebooks/deepnote/integrations/federatedAuth/federatedAuthTestHelpers.ts index 2b03fef2ff..6e4266ca8b 100644 --- a/src/notebooks/deepnote/integrations/federatedAuth/federatedAuthTestHelpers.ts +++ b/src/notebooks/deepnote/integrations/federatedAuth/federatedAuthTestHelpers.ts @@ -130,7 +130,7 @@ export function settleAsyncHandlers(ms = DEFAULT_SETTLE_DELAY_MS): Promise return new Promise((resolve) => setTimeout(resolve, ms)); } -/** Inverse of `escapePythonString`: parses a Python single-quoted literal back to its source string. */ +/** Parses a Python single-quoted literal, decoding only `\\`, `\'` and `\n`; other escapes are kept verbatim. */ export function parsePythonSingleQuoted(escaped: string): string { if (!escaped.startsWith("'") || !escaped.endsWith("'")) { throw new Error('must be wrapped in single quotes'); diff --git a/src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.ts b/src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.ts deleted file mode 100644 index 0eff3f8b1c..0000000000 --- a/src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.ts +++ /dev/null @@ -1,103 +0,0 @@ -// VENDORED from @deepnote/blocks bundled internals. None of these symbols are part of the -// package's public exports (verified against dist/index.d.ts). -// once @deepnote/blocks exports them, delete this file and import directly. -// TODO(deepnote-followups): remove when @deepnote/blocks exports these helpers. - -import type { DeepnoteBlock } from '@deepnote/blocks'; -import { dedent } from 'ts-dedent'; - -/** - * SQL block subtype. Vendored because `@deepnote/blocks` does not export `SqlBlock`. - * TODO(deepnote-followups): replace with `import { SqlBlock } from '@deepnote/blocks'` once exported upstream. - */ -export type SqlBlock = Extract; - -/** - * Valid `sql_cache_mode` values for `_dntk.execute_sql_with_connection_json`. - * TODO(deepnote-followups): remove when @deepnote/blocks exports this. - */ -export type SqlCacheMode = 'cache_disabled' | 'always_write' | 'read_or_write'; - -/** - * Valid `return_variable_type` values for `_dntk.execute_sql_with_connection_json`. - * TODO(deepnote-followups): remove when @deepnote/blocks exports this. - */ -export type SqlCellVariableType = 'dataframe' | 'query_preview'; - -/** - * Mirror of upstream's bundled-internal `escapePythonString`: single-quotes the string and escapes `\`, `'`, `\n`. - * TODO(deepnote-followups): remove when @deepnote/blocks exports this. - */ -export function escapePythonString(value: string): string { - return `'${value.replaceAll('\\', '\\\\').replaceAll("'", "\\'").replaceAll('\n', '\\n')}'`; -} - -/** - * Mirror of upstream's `sanitizePythonVariableName` (whitespace→`_`, strips non-identifier chars, fallback `'input_1'`). - * Differs from upstream by accepting `undefined` and returning `undefined`. - * TODO(deepnote-followups): remove when @deepnote/blocks exports this. - */ -export function sanitizePythonVariableName(name: string | undefined): string | undefined { - if (name === undefined) { - return undefined; - } - - let sanitizedVariableName = name - .replace(/\s+/g, '_') - .replace(/[^0-9a-zA-Z_]/g, '') - .replace(/^[^a-zA-Z_]+/g, ''); - - if (sanitizedVariableName === '') { - sanitizedVariableName = 'input_1'; - } - - return sanitizedVariableName; -} - -/** - * Mirror of upstream's `createDataFrameConfig`: produces a two-branch Python snippet configuring the dataframe formatter. - * TODO(deepnote-followups): remove when @deepnote/blocks exports this. - */ -export function createDataFrameConfig(block: SqlBlock): string { - const tableState = block.metadata?.deepnote_table_state ?? {}; - const tableStateAsJson = JSON.stringify(tableState); - - return dedent` - if '_dntk' in globals(): - _dntk.dataframe_utils.configure_dataframe_formatter(${escapePythonString(tableStateAsJson)}) - else: - _deepnote_current_table_attrs = ${escapePythonString(tableStateAsJson)} - `; -} - -/** - * Mirror of upstream's `executeSqlQueryWithConnectionJson` ([code-snippets.ts](file:///workspace/deepnote-internal/libs/shared/src/cells/code-snippets.ts) lines 91–123). - * Differs by passing `connectionJson` through {@link escapePythonString} so hostile JSON content (backslashes, newlines, single quotes) cannot break the Python literal. - * TODO(deepnote-followups): remove when @deepnote/blocks exports this. - */ -export function executeSqlQueryWithConnectionJson(params: { - query: string; - auditComment?: string; - connectionJson: string; - pythonVariableName?: string; - sqlCacheMode: SqlCacheMode; - returnVariableType: SqlCellVariableType; -}): string { - const escapedQuery = escapePythonString(params.query); - const escapedAuditComment = escapePythonString(params.auditComment ?? ''); - const escapedConnectionJson = escapePythonString(params.connectionJson); - const executeSqlFunctionCall = dedent`_dntk.execute_sql_with_connection_json( - ${escapedQuery}, - ${escapedConnectionJson}, - audit_sql_comment=${escapedAuditComment}, - sql_cache_mode='${params.sqlCacheMode}', - return_variable_type='${params.returnVariableType}' - )`; - - return params.pythonVariableName === undefined - ? executeSqlFunctionCall - : dedent` - ${params.pythonVariableName} = ${executeSqlFunctionCall} - ${params.pythonVariableName} - `; -} diff --git a/src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.unit.test.ts b/src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.unit.test.ts deleted file mode 100644 index 31c8d72419..0000000000 --- a/src/notebooks/deepnote/integrations/federatedAuth/vendoredBlocksHelpers.unit.test.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { createPythonCode } from '@deepnote/blocks'; -import { assert } from 'chai'; - -import { - createDataFrameConfig, - escapePythonString, - sanitizePythonVariableName, - SqlBlock -} from './vendoredBlocksHelpers'; -import { parsePythonSingleQuoted } from './federatedAuthTestHelpers'; - -suite('vendoredBlocksHelpers', () => { - suite('escapePythonString', () => { - test('handles a mixed input combining backslash, quote, and newline', () => { - const input = `a\\b'c\nd`; - // `\\` → `\\\\`, `'` → `\'`, `\n` → `\\n`. - assert.strictEqual(escapePythonString(input), "'a\\\\b\\'c\\nd'"); - }); - - test('does not escape tabs', () => { - // Upstream only escapes `\`, `'`, `\n`; tabs pass through verbatim. - assert.strictEqual(escapePythonString('a\tb'), "'a\tb'"); - }); - - test('output, when interpreted as a Python single-quoted literal, round-trips back to the original SQL query', () => { - // Catches: a future change adding an extra escape (e.g. `\t`/`\r`) without updating the inverse mapping, breaking SQL queries at runtime. - const queries = [ - "SELECT 'a''b' AS x", - 'SELECT * FROM t WHERE path = "C:\\Users\\me"', - 'SELECT\n *\nFROM\n table', - "SELECT 'café' AS greeting, '世界' AS world", - "SELECT 'a\\b' AS literal_backslash", - '' - ]; - for (const query of queries) { - const escaped = escapePythonString(query); - assert.strictEqual( - parsePythonSingleQuoted(escaped), - query, - `round-trip failed for: ${JSON.stringify(query)}` - ); - } - }); - }); - - suite('sanitizePythonVariableName', () => { - test('returns undefined for undefined input', () => { - assert.strictEqual(sanitizePythonVariableName(undefined), undefined); - }); - - test('falls back to "input_1" for an empty string', () => { - assert.strictEqual(sanitizePythonVariableName(''), 'input_1'); - }); - - test('strips leading non-identifier characters but keeps a following underscore', () => { - // Upstream strips `[^a-zA-Z_]+` from the start, so `123_foo` → `_foo` (underscore is a valid leading char) and `1abc` → `abc`. - assert.strictEqual(sanitizePythonVariableName('1abc'), 'abc'); - assert.strictEqual(sanitizePythonVariableName('123_foo'), '_foo'); - }); - - test('converts whitespace to underscores', () => { - assert.strictEqual(sanitizePythonVariableName('my var'), 'my_var'); - }); - - test('strips hyphens and dots', () => { - assert.strictEqual(sanitizePythonVariableName('my-var.name'), 'myvarname'); - }); - - test('passes a valid identifier through unchanged', () => { - assert.strictEqual(sanitizePythonVariableName('valid_name_1'), 'valid_name_1'); - }); - - test('preserves a leading underscore', () => { - assert.strictEqual(sanitizePythonVariableName('_hidden'), '_hidden'); - }); - - test('falls back to "input_1" when only invalid chars are present', () => { - // Upstream behavior: '-' is stripped, then nothing remains, so fallback applies. - assert.strictEqual(sanitizePythonVariableName('---'), 'input_1'); - }); - - test('collapses an all-whitespace string to a single underscore', () => { - // Catches: a future upstream change to the `\s+` → `_` step (e.g. `\W+`) breaking parity. - assert.strictEqual(sanitizePythonVariableName(' '), '_'); - }); - }); - - suite('createDataFrameConfig', () => { - function makeSqlBlock(tableState?: Record): SqlBlock { - return { - type: 'sql', - id: 'block-id', - blockGroup: 'group-id', - sortingKey: 'a', - content: 'SELECT 1', - metadata: tableState === undefined ? {} : { deepnote_table_state: tableState } - } as unknown as SqlBlock; - } - - test('uses an empty JSON object when metadata.deepnote_table_state is missing', () => { - const block = makeSqlBlock(); - const result = createDataFrameConfig(block); - - const expected = - "if '_dntk' in globals():\n" + - " _dntk.dataframe_utils.configure_dataframe_formatter('{}')\n" + - 'else:\n' + - " _deepnote_current_table_attrs = '{}'"; - - assert.strictEqual(result, expected); - }); - - test('JSON-stringifies a non-trivial table state and round-trips through escapePythonString', () => { - const tableState = { - pageSize: 50, - sortBy: [{ column: 'name', direction: 'asc' as const }], - hiddenColumns: ['id'] - }; - const block = makeSqlBlock(tableState); - const result = createDataFrameConfig(block); - - const expectedJson = JSON.stringify(tableState); - assert.include(result, escapePythonString(expectedJson)); - // Both branches must reference the same escaped JSON. - const occurrences = result.split(escapePythonString(expectedJson)).length - 1; - assert.strictEqual(occurrences, 2); - }); - - test('matches the data-frame-config prefix produced by upstream @deepnote/blocks.createPythonCode', () => { - // Catches: an upstream change to the dataframe-config template (indentation, wording, JSON ordering) drifting us out of parity. Upstream emits `\n\n`; we compare the prefix up to the blank line. - const tableState = { - pageSize: 50, - sortBy: [{ column: 'name', direction: 'asc' as const }], - hiddenColumns: ['id'] - }; - const block = makeSqlBlock(tableState); - - const ours = createDataFrameConfig(block); - const upstreamFull = createPythonCode(block); - const upstreamPrefix = upstreamFull.split('\n\n')[0]; - - assert.strictEqual(ours, upstreamPrefix); - }); - }); -});