@deepnote/database-integrations 1.6.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import z, { z as z$1 } from "zod";
2
2
  import { isMap, isSeq, parseDocument } from "yaml";
3
3
  import { parseYaml } from "@deepnote/blocks";
4
-
5
4
  //#region src/sql-integration-auth-methods.ts
6
5
  const AwsAuthMethods = {
7
6
  AccessKeys: "access-keys",
@@ -42,7 +41,6 @@ function isFederatedAuthMethod(authMethod) {
42
41
  function isFederatedAuthMetadata(metadata) {
43
42
  return metadata.authMethod !== void 0 && isFederatedAuthMethod(metadata.authMethod);
44
43
  }
45
-
46
44
  //#endregion
47
45
  //#region src/database-integration-metadata-schemas.ts
48
46
  const athenaMetadataSchema = z$1.object({
@@ -270,8 +268,6 @@ const pgsqlMetadataSchema = commonDatabaseSchema.extend({
270
268
  caCertificateName: z$1.string().optional(),
271
269
  caCertificateText: z$1.string().optional()
272
270
  });
273
- const materializeMetadataSchema = pgsqlMetadataSchema.extend({ cluster: z$1.string() });
274
- const sqlServerMetadataSchema = commonDatabaseSchema.extend({ port: z$1.string() });
275
271
  const databaseMetadataSchemasByType = {
276
272
  "alloydb": alloydbMetadataSchema,
277
273
  "athena": athenaMetadataSchema,
@@ -281,7 +277,7 @@ const databaseMetadataSchemasByType = {
281
277
  "databricks": databricksMetadataSchema,
282
278
  "dremio": dremioMetadataSchema,
283
279
  "mariadb": mariadbMetadataSchema,
284
- "materialize": materializeMetadataSchema,
280
+ "materialize": pgsqlMetadataSchema.extend({ cluster: z$1.string() }),
285
281
  "mindsdb": mindsdbMetadataSchema,
286
282
  "mongodb": mongodbMetadataSchema,
287
283
  "mysql": mysqlMetadataSchema,
@@ -290,10 +286,9 @@ const databaseMetadataSchemasByType = {
290
286
  "redshift": redshiftMetadataSchema,
291
287
  "snowflake": snowflakeMetadataSchema,
292
288
  "spanner": spannerMetadataSchema,
293
- "sql-server": sqlServerMetadataSchema,
289
+ "sql-server": commonDatabaseSchema.extend({ port: z$1.string() }),
294
290
  "trino": trinoMetadataSchema
295
291
  };
296
-
297
292
  //#endregion
298
293
  //#region src/database-integration-config.ts
299
294
  const commonIntegrationConfig = z.object({
@@ -408,7 +403,6 @@ const databaseIntegrationConfigSchema = z.discriminatedUnion("type", [
408
403
  federated_auth_method: z.enum(["trino-oauth", "password"]).optional().nullable()
409
404
  })
410
405
  ]);
411
-
412
406
  //#endregion
413
407
  //#region src/snowflake-integration-env-vars.ts
414
408
  /**
@@ -474,7 +468,6 @@ function createSqlAlchemyInputFromUrl(url, keyPair) {
474
468
  param_style: "pyformat"
475
469
  };
476
470
  }
477
-
478
471
  //#endregion
479
472
  //#region src/database-integration-env-vars.ts
480
473
  function getEnvironmentVariablesForIntegrations(integrations, params) {
@@ -839,7 +832,6 @@ function getSqlEnvVarName(integrationId) {
839
832
  function assertNever(value) {
840
833
  throw new Error(`Unexpected value: ${value}`);
841
834
  }
842
-
843
835
  //#endregion
844
836
  //#region src/database-integration-types.ts
845
837
  const sqlIntegrationTypes = [
@@ -884,7 +876,6 @@ function isSqlIntegrationType(type) {
884
876
  function isDatabaseIntegrationTypeWithSslSupport(type) {
885
877
  return databaseIntegrationTypesWithSslSupport.includes(type);
886
878
  }
887
-
888
879
  //#endregion
889
880
  //#region src/loading/api-error.ts
890
881
  /**
@@ -898,18 +889,31 @@ var ApiError = class extends Error {
898
889
  this.statusCode = statusCode;
899
890
  }
900
891
  };
901
-
902
892
  //#endregion
903
893
  //#region src/loading/constants.ts
904
- /** Built-in integrations that don't require external configuration. */
905
- const BUILTIN_INTEGRATIONS = new Set(["deepnote-dataframe-sql", "pandas-dataframe"]);
894
+ /** Built-in integrations that don't require external configuration. Keys are canonical lowercase IDs. */
895
+ const BUILTIN_INTEGRATIONS = /* @__PURE__ */ new Set(["deepnote-dataframe-sql", "pandas-dataframe"]);
896
+ /**
897
+ * Return whether the given integration ID refers to a built-in integration.
898
+ *
899
+ * The comparison is case-insensitive: built-in IDs are stored as canonical
900
+ * lowercase strings in {@link BUILTIN_INTEGRATIONS}, and SQL block metadata may
901
+ * reference them in any casing (e.g. `Pandas-DataFrame`). This mirrors the
902
+ * case-insensitive env-var derivation in `getSqlEnvVarName`, so a built-in is
903
+ * recognized regardless of how a notebook cased its `sql_integration_id`.
904
+ *
905
+ * @param integrationId - The integration ID from SQL block metadata.
906
+ * @returns `true` when the ID matches a built-in integration regardless of casing.
907
+ */
908
+ function isBuiltinIntegration(integrationId) {
909
+ return BUILTIN_INTEGRATIONS.has(integrationId.toLowerCase());
910
+ }
906
911
  /** Default `.env` file name for storing integration secrets. */
907
912
  const DEFAULT_ENV_FILE = ".env";
908
913
  /** Default integrations file name. */
909
914
  const DEFAULT_INTEGRATIONS_FILE = ".deepnote.env.yaml";
910
915
  /** Default Deepnote API base URL. */
911
916
  const DEFAULT_API_URL = "https://api.deepnote.com";
912
-
913
917
  //#endregion
914
918
  //#region src/loading/env-var-refs.ts
915
919
  const ENV_VAR_REF_PREFIX = "env:";
@@ -918,7 +922,7 @@ function generateEnvVarName(integrationId, fieldPath) {
918
922
  }
919
923
  function parseEnvVarRef(value) {
920
924
  if (typeof value !== "string") return null;
921
- if (!value.startsWith(ENV_VAR_REF_PREFIX)) return null;
925
+ if (!value.startsWith("env:")) return null;
922
926
  const varName = value.slice(4);
923
927
  if (!varName) return null;
924
928
  return {
@@ -981,7 +985,6 @@ function resolveEnvVarRefsFromMap(obj, vars, currentPath) {
981
985
  }
982
986
  return obj;
983
987
  }
984
-
985
988
  //#endregion
986
989
  //#region src/loading/fetch-integrations.ts
987
990
  /**
@@ -1012,14 +1015,17 @@ function trimTrailingSlash(url) {
1012
1015
  *
1013
1016
  * @param baseUrl - The base URL of the Deepnote API
1014
1017
  * @param token - The authentication token
1015
- * @param integrationIds - Optional list of integration IDs to fetch. When provided, only these integrations are returned.
1018
+ * @param integrationIds - Optional list of integration UUIDs to fetch. When provided, only these integrations are returned.
1016
1019
  * @returns Array of integrations from the API
1017
1020
  * @throws ApiError if the request fails
1018
1021
  */
1019
1022
  async function fetchIntegrations(baseUrl, token, integrationIds) {
1020
1023
  const endpoint = new URL(`${trimTrailingSlash(baseUrl)}/v2/integrations`);
1021
1024
  endpoint.searchParams.set("includeMetadata", "true");
1022
- if (integrationIds && integrationIds.length > 0) endpoint.searchParams.set("integrationIds", integrationIds.join(","));
1025
+ if (integrationIds && integrationIds.length > 0) {
1026
+ const normalizedIntegrationIds = new Set(integrationIds.map((id) => id.toLowerCase()));
1027
+ endpoint.searchParams.set("integrationIds", Array.from(normalizedIntegrationIds).join(","));
1028
+ }
1023
1029
  const url = endpoint.toString();
1024
1030
  const response = await fetch(url, {
1025
1031
  method: "GET",
@@ -1039,21 +1045,38 @@ async function fetchIntegrations(baseUrl, token, integrationIds) {
1039
1045
  if (!parseResult.success) throw new Error(`Invalid API response: ${parseResult.error.message}`);
1040
1046
  return parseResult.data.integrations;
1041
1047
  }
1042
-
1043
1048
  //#endregion
1044
1049
  //#region src/loading/integrations-document.ts
1045
1050
  /**
1051
+ * Error thrown when integrations YAML content cannot be parsed.
1052
+ */
1053
+ var IntegrationsYamlParseError = class extends Error {
1054
+ /** The underlying parse errors reported by the `yaml` library. */
1055
+ errors;
1056
+ constructor(errors) {
1057
+ super(`Failed to parse integrations YAML: ${errors[0]?.message ?? "unknown parse error"}`);
1058
+ this.name = "IntegrationsYamlParseError";
1059
+ this.errors = errors;
1060
+ }
1061
+ };
1062
+ /**
1046
1063
  * Parse integrations YAML content into a mutable `yaml` Document that preserves
1047
1064
  * comments and formatting. Returns `null` for empty content.
1048
1065
  *
1049
1066
  * This is the content-accepting counterpart of the Node-only `readIntegrationsDocument`.
1067
+ *
1068
+ * @throws {IntegrationsYamlParseError} If the content is not valid YAML. Malformed
1069
+ * content is rejected here rather than at serialization time, where `yaml` would
1070
+ * otherwise fail with an opaque "Document with errors cannot be stringified".
1050
1071
  */
1051
1072
  function parseIntegrationsDocument(content) {
1052
1073
  if (!content.trim()) return null;
1053
- return parseDocument(content, {
1074
+ const doc = parseDocument(content, {
1054
1075
  strict: true,
1055
1076
  version: "1.2"
1056
1077
  });
1078
+ if (doc.errors.length > 0) throw new IntegrationsYamlParseError(doc.errors);
1079
+ return doc;
1057
1080
  }
1058
1081
  /**
1059
1082
  * Serialize an integrations Document back to YAML text, preserving comments and
@@ -1062,7 +1085,6 @@ function parseIntegrationsDocument(content) {
1062
1085
  function serializeIntegrationsDocument(doc) {
1063
1086
  return doc.toString({ lineWidth: 0 });
1064
1087
  }
1065
-
1066
1088
  //#endregion
1067
1089
  //#region src/loading/integrations-file-schema.ts
1068
1090
  /**
@@ -1083,7 +1105,6 @@ function serializeIntegrationsDocument(doc) {
1083
1105
  */
1084
1106
  const baseIntegrationsFileSchema = z$1.object({ integrations: z$1.array(z$1.record(z$1.unknown())).optional().default([]) });
1085
1107
  const integrationsFileSchema = z$1.object({ integrations: z$1.array(databaseIntegrationConfigSchema) });
1086
-
1087
1108
  //#endregion
1088
1109
  //#region src/secret-field-paths.ts
1089
1110
  /**
@@ -1142,7 +1163,6 @@ const SECRET_PATHS = {
1142
1163
  function getSecretFieldPaths(type) {
1143
1164
  return SECRET_PATHS[type] ?? [];
1144
1165
  }
1145
-
1146
1166
  //#endregion
1147
1167
  //#region src/loading/merge-integrations.ts
1148
1168
  /**
@@ -1212,7 +1232,7 @@ function updateIntegrationMetadataMap({ metadataMap, integrationId, integrationM
1212
1232
  * @returns Record of extracted secrets (envVarName -> secretValue)
1213
1233
  */
1214
1234
  function updateIntegrationInDocument(doc, integrationMap, integration) {
1215
- const { id, name, type, metadata, federated_auth_method,...rest } = integration;
1235
+ const { id, name, type, metadata, federated_auth_method, ...rest } = integration;
1216
1236
  integrationMap.set("id", id);
1217
1237
  integrationMap.set("name", name);
1218
1238
  integrationMap.set("type", type);
@@ -1235,7 +1255,7 @@ function updateIntegrationInDocument(doc, integrationMap, integration) {
1235
1255
  */
1236
1256
  function addIntegrationToSeq(doc, integrationsSeq, integration) {
1237
1257
  const secretPaths = getSecretFieldPaths(integration.type);
1238
- const { metadata,...restIntegration } = integration;
1258
+ const { metadata, ...restIntegration } = integration;
1239
1259
  const integrationMap = doc.createNode(restIntegration);
1240
1260
  const metadataMap = doc.createNode({});
1241
1261
  const secrets = updateIntegrationMetadataMap({
@@ -1362,7 +1382,6 @@ function mergeApiIntegrationsIntoDocument(doc, apiIntegrations) {
1362
1382
  skipped
1363
1383
  };
1364
1384
  }
1365
-
1366
1385
  //#endregion
1367
1386
  //#region src/loading/merge-into-yaml.ts
1368
1387
  /**
@@ -1376,6 +1395,14 @@ function mergeApiIntegrationsIntoDocument(doc, apiIntegrations) {
1376
1395
  *
1377
1396
  * @param existingContent - Current YAML content, or `null` if the file doesn't exist yet
1378
1397
  * @param apiIntegrations - Integrations fetched from the API
1398
+ *
1399
+ * @throws {IntegrationsYamlParseError} If `existingContent` is not valid YAML (for
1400
+ * example, unresolved git merge conflict markers). Callers that want to rebuild the
1401
+ * file from scratch instead of surfacing the error can catch it and re-run with
1402
+ * `mergeApiIntegrationsIntoYaml(null, apiIntegrations)` — note this discards the
1403
+ * existing content, including any local-only integrations and comments.
1404
+ * @throws {InvalidIntegrationsTypeError} If the existing `integrations` property is
1405
+ * present but is not a list.
1379
1406
  */
1380
1407
  function mergeApiIntegrationsIntoYaml(existingContent, apiIntegrations) {
1381
1408
  const doc = (existingContent != null ? parseIntegrationsDocument(existingContent) : null) ?? createNewDocument();
@@ -1388,7 +1415,6 @@ function mergeApiIntegrationsIntoYaml(existingContent, apiIntegrations) {
1388
1415
  skipped
1389
1416
  };
1390
1417
  }
1391
-
1392
1418
  //#endregion
1393
1419
  //#region src/loading/parse-integrations.ts
1394
1420
  /**
@@ -1477,6 +1503,5 @@ function parseIntegrations({ yaml, env = {} }) {
1477
1503
  issues
1478
1504
  };
1479
1505
  }
1480
-
1481
1506
  //#endregion
1482
- export { ApiError, AwsAuthMethods, BUILTIN_INTEGRATIONS, BigQueryAuthMethods, BigQueryServiceAccountParseError, DEFAULT_API_URL, DEFAULT_ENV_FILE, DEFAULT_INTEGRATIONS_FILE, DatabaseAuthMethods, ENV_VAR_REF_PREFIX, EnvVarResolutionError, InvalidIntegrationError, InvalidIntegrationsTypeError, JSON_SCHEMA_URL, SCHEMA_COMMENT, SnowflakeAuthMethods, SpannerServiceAccountParseError, TrinoAuthMethods, addIntegrationToSeq, apiIntegrationSchema, apiResponseSchema, baseIntegrationsFileSchema, convertApiIntegrations, createEnvVarRef, createNewDocument, databaseIntegrationConfigSchema, databaseIntegrationTypes, databaseIntegrationTypesWithSslSupport, databaseMetadataSchemasByType, extractEnvVarName, federatedAuthMethods, fetchIntegrations, generateEnvVarName, getEnvironmentVariablesForIntegrations, getOrCreateIntegrationMetadata, getOrCreateIntegrationsFromDocument, getSecretFieldPaths, getSnowflakeFederatedAuthSqlAlchemyInput, getSqlAlchemyInput, getSqlEnvVarName, integrationsFileSchema, isDatabaseIntegrationType, isDatabaseIntegrationTypeWithSslSupport, isEnvVarRef, isFederatedAuthMetadata, isFederatedAuthMethod, isSqlIntegrationType, mergeApiIntegrationsIntoDocument, mergeApiIntegrationsIntoYaml, mergeProcessedIntegrations, parseEnvVarRef, parseIntegrations, parseIntegrationsDocument, resolveEnvVarRefsFromMap, serializeIntegrationsDocument, sqlIntegrationTypes, updateIntegrationInDocument, updateIntegrationMetadataMap };
1507
+ export { ApiError, AwsAuthMethods, BUILTIN_INTEGRATIONS, BigQueryAuthMethods, BigQueryServiceAccountParseError, DEFAULT_API_URL, DEFAULT_ENV_FILE, DEFAULT_INTEGRATIONS_FILE, DatabaseAuthMethods, ENV_VAR_REF_PREFIX, EnvVarResolutionError, IntegrationsYamlParseError, InvalidIntegrationError, InvalidIntegrationsTypeError, JSON_SCHEMA_URL, SCHEMA_COMMENT, SnowflakeAuthMethods, SpannerServiceAccountParseError, TrinoAuthMethods, addIntegrationToSeq, apiIntegrationSchema, apiResponseSchema, baseIntegrationsFileSchema, convertApiIntegrations, createEnvVarRef, createNewDocument, databaseIntegrationConfigSchema, databaseIntegrationTypes, databaseIntegrationTypesWithSslSupport, databaseMetadataSchemasByType, extractEnvVarName, federatedAuthMethods, fetchIntegrations, generateEnvVarName, getEnvironmentVariablesForIntegrations, getOrCreateIntegrationMetadata, getOrCreateIntegrationsFromDocument, getSecretFieldPaths, getSnowflakeFederatedAuthSqlAlchemyInput, getSqlAlchemyInput, getSqlEnvVarName, integrationsFileSchema, isBuiltinIntegration, isDatabaseIntegrationType, isDatabaseIntegrationTypeWithSslSupport, isEnvVarRef, isFederatedAuthMetadata, isFederatedAuthMethod, isSqlIntegrationType, mergeApiIntegrationsIntoDocument, mergeApiIntegrationsIntoYaml, mergeProcessedIntegrations, parseEnvVarRef, parseIntegrations, parseIntegrationsDocument, resolveEnvVarRefsFromMap, serializeIntegrationsDocument, sqlIntegrationTypes, updateIntegrationInDocument, updateIntegrationMetadataMap };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepnote/database-integrations",
3
- "version": "1.6.0",
3
+ "version": "1.7.1",
4
4
  "description": "Deepnote database integration definitions",
5
5
  "keywords": [],
6
6
  "repository": {
@@ -27,17 +27,17 @@
27
27
  "package.json"
28
28
  ],
29
29
  "dependencies": {
30
- "yaml": "^2.8.3",
30
+ "yaml": "^2.9.0",
31
31
  "zod": "3.25.76",
32
- "@deepnote/blocks": "4.7.0"
32
+ "@deepnote/blocks": "4.8.1"
33
33
  },
34
34
  "publishConfig": {
35
35
  "access": "public",
36
36
  "registry": "https://registry.npmjs.org"
37
37
  },
38
38
  "scripts": {
39
- "build": "tsdown --format esm --format cjs --dts",
39
+ "build": "tsdown --format esm --format cjs --dts --no-fixed-extension",
40
40
  "test": "vitest",
41
- "watch": "tsdown --watch --format esm --format cjs --dts"
41
+ "watch": "tsdown --watch --format esm --format cjs --dts --no-fixed-extension"
42
42
  }
43
43
  }