@sqlanvil/cli 1.35.0 → 1.36.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/README.md CHANGED
@@ -12,7 +12,7 @@ authority for the version you have installed: `sqlanvil help` lists the commands
12
12
 
13
13
  ```bash
14
14
  npm install -g @sqlanvil/cli
15
- sqlanvil --version # sqlanvil 1.35.0 (Dataform core 3.1.0)
15
+ sqlanvil --version # sqlanvil 1.36.0 (Dataform core 3.1.0)
16
16
  ```
17
17
 
18
18
  Requires Node.js 20.19+ or 22.12+. `--version` reports the sqlanvil release and the Dataform core
@@ -108,10 +108,12 @@ Installs the NPM dependencies declared in the project's `package.json` (for exam
108
108
  sqlanvil init-creds [project-dir]
109
109
  ```
110
110
 
111
- **BigQuery only.** Asks for the dataset location and whether to use Application Default
112
- Credentials or a service-account JSON key, then writes `.df-credentials.json`. For Postgres,
113
- Supabase and MySQL, edit the template `init` wrote instead (see
114
- [Credentials file](#credentials-file)).
111
+ **BigQuery only.** Asks for the dataset location, whether to use Application Default
112
+ Credentials or a service-account JSON key, and (optional, press Enter to skip) a service account
113
+ to impersonate — saved as `impersonateServiceAccount`, see [Credentials file](#credentials-file) —
114
+ then writes `.df-credentials.json`. The connection test runs as the impersonated account, so a
115
+ missing `roles/iam.serviceAccountTokenCreator` grant surfaces before the file is written. For
116
+ Postgres, Supabase and MySQL, edit the template `init` wrote instead.
115
117
 
116
118
  | Flag | Default | Description |
117
119
  |---|---|---|
@@ -418,7 +420,11 @@ It is gitignored by `init` and must never be committed. Non-secret settings stay
418
420
 
419
421
  `credentials` is the service-account key JSON as a string. Omit it to use Application Default
420
422
  Credentials (`gcloud auth application-default login`). `accessToken` takes a short-lived OAuth2
421
- token instead, and wins over `credentials`.
423
+ token instead, and wins over `credentials`. `impersonateServiceAccount` (a service-account email)
424
+ makes the CLI run BigQuery queries as that account without holding its key: your ADC identity, or
425
+ the `credentials` key, is the source and needs `roles/iam.serviceAccountTokenCreator` on it.
426
+ Precedence: `accessToken` → `impersonateServiceAccount` → `credentials` → ADC. The same fields work
427
+ in a `connections.<name>` BigQuery entry.
422
428
 
423
429
  Two optional sections can sit alongside the warehouse fields:
424
430
 
package/bundle.js CHANGED
@@ -49829,6 +49829,7 @@ function requireTs () {
49829
49829
  * @property {string|null} [credentials] BigQuery credentials
49830
49830
  * @property {string|null} [location] BigQuery location
49831
49831
  * @property {string|null} [accessToken] BigQuery accessToken
49832
+ * @property {string|null} [impersonateServiceAccount] BigQuery impersonateServiceAccount
49832
49833
  */
49833
49834
 
49834
49835
  /**
@@ -49878,6 +49879,14 @@ function requireTs () {
49878
49879
  */
49879
49880
  BigQuery.prototype.accessToken = "";
49880
49881
 
49882
+ /**
49883
+ * BigQuery impersonateServiceAccount.
49884
+ * @member {string} impersonateServiceAccount
49885
+ * @memberof sqlanvil.BigQuery
49886
+ * @instance
49887
+ */
49888
+ BigQuery.prototype.impersonateServiceAccount = "";
49889
+
49881
49890
  /**
49882
49891
  * Creates a new BigQuery instance using the specified properties.
49883
49892
  * @function create
@@ -49914,6 +49923,8 @@ function requireTs () {
49914
49923
  writer.uint32(/* id 4, wireType 2 =*/34).string(message.location);
49915
49924
  if (message.accessToken != null && Object.hasOwnProperty.call(message, "accessToken"))
49916
49925
  writer.uint32(/* id 5, wireType 2 =*/42).string(message.accessToken);
49926
+ if (message.impersonateServiceAccount != null && Object.hasOwnProperty.call(message, "impersonateServiceAccount"))
49927
+ writer.uint32(/* id 6, wireType 2 =*/50).string(message.impersonateServiceAccount);
49917
49928
  return writer;
49918
49929
  };
49919
49930
 
@@ -49980,6 +49991,10 @@ function requireTs () {
49980
49991
  message.accessToken = reader.string();
49981
49992
  break;
49982
49993
  }
49994
+ case 6: {
49995
+ message.impersonateServiceAccount = reader.string();
49996
+ break;
49997
+ }
49983
49998
  default:
49984
49999
  reader.skipType(tag & 7, long);
49985
50000
  break;
@@ -50036,6 +50051,9 @@ function requireTs () {
50036
50051
  if (message.accessToken != null && Object.hasOwnProperty.call(message, "accessToken"))
50037
50052
  if (!$util.isString(message.accessToken))
50038
50053
  return "accessToken: string expected";
50054
+ if (message.impersonateServiceAccount != null && Object.hasOwnProperty.call(message, "impersonateServiceAccount"))
50055
+ if (!$util.isString(message.impersonateServiceAccount))
50056
+ return "impersonateServiceAccount: string expected";
50039
50057
  return null;
50040
50058
  };
50041
50059
 
@@ -50065,6 +50083,8 @@ function requireTs () {
50065
50083
  message.location = String(object.location);
50066
50084
  if (object.accessToken != null)
50067
50085
  message.accessToken = String(object.accessToken);
50086
+ if (object.impersonateServiceAccount != null)
50087
+ message.impersonateServiceAccount = String(object.impersonateServiceAccount);
50068
50088
  return message;
50069
50089
  };
50070
50090
 
@@ -50090,6 +50110,7 @@ function requireTs () {
50090
50110
  object.credentials = "";
50091
50111
  object.location = "";
50092
50112
  object.accessToken = "";
50113
+ object.impersonateServiceAccount = "";
50093
50114
  }
50094
50115
  if (message.projectId != null && Object.hasOwnProperty.call(message, "projectId"))
50095
50116
  object.projectId = message.projectId;
@@ -50099,6 +50120,8 @@ function requireTs () {
50099
50120
  object.location = message.location;
50100
50121
  if (message.accessToken != null && Object.hasOwnProperty.call(message, "accessToken"))
50101
50122
  object.accessToken = message.accessToken;
50123
+ if (message.impersonateServiceAccount != null && Object.hasOwnProperty.call(message, "impersonateServiceAccount"))
50124
+ object.impersonateServiceAccount = message.impersonateServiceAccount;
50102
50125
  return object;
50103
50126
  };
50104
50127
 
@@ -55101,7 +55124,7 @@ function requireVersion () {
55101
55124
  hasRequiredVersion = 1;
55102
55125
  Object.defineProperty(version, "__esModule", { value: true });
55103
55126
  version.dataformVersion = version.version = void 0;
55104
- version.version = "1.35.0";
55127
+ version.version = "1.36.0";
55105
55128
  version.dataformVersion = "3.1.0";
55106
55129
 
55107
55130
  return version;
@@ -56973,7 +56996,9 @@ function requireBigquery () {
56973
56996
  return (mod && mod.__esModule) ? mod : { "default": mod };
56974
56997
  };
56975
56998
  Object.defineProperty(bigquery, "__esModule", { value: true });
56976
- bigquery.BigQueryDbAdapter = void 0;
56999
+ bigquery.BigQueryDbAdapter = bigquery.DeferredAuthClient = void 0;
57000
+ bigquery.createImpersonatedClient = createImpersonatedClient;
57001
+ bigquery.createAuthClient = createAuthClient;
56977
57002
  bigquery.bigQueryClientOptions = bigQueryClientOptions;
56978
57003
  bigquery.createBigQueryClientProvider = createBigQueryClientProvider;
56979
57004
  const bigquery_1 = require$$0$6;
@@ -56986,17 +57011,54 @@ function requireBigquery () {
56986
57011
  const errors_1 = requireErrors();
56987
57012
  const promises_1 = requirePromises();
56988
57013
  const ts_1 = requireTs();
57014
+ const CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
56989
57015
  const EXTRA_GOOGLE_SCOPES = ["https://www.googleapis.com/auth/drive"];
56990
- function bigQueryClientOptions(credentials, projectId) {
57016
+ class DeferredAuthClient extends google_auth_library_1.AuthClient {
57017
+ constructor(resolveClient) {
57018
+ super();
57019
+ this.resolveClient = resolveClient;
57020
+ }
57021
+ async getAccessToken() {
57022
+ return (await this.resolveClient()).getAccessToken();
57023
+ }
57024
+ async getRequestHeaders(url) {
57025
+ return (await this.resolveClient()).getRequestHeaders(url);
57026
+ }
57027
+ async request(options) {
57028
+ return (await this.resolveClient()).request(options);
57029
+ }
57030
+ }
57031
+ bigquery.DeferredAuthClient = DeferredAuthClient;
57032
+ function createImpersonatedClient(targetPrincipal, sourceClient) {
57033
+ return new google_auth_library_1.Impersonated({
57034
+ sourceClient,
57035
+ targetPrincipal,
57036
+ targetScopes: [CLOUD_PLATFORM_SCOPE, ...EXTRA_GOOGLE_SCOPES],
57037
+ });
57038
+ }
57039
+ function createAuthClient(credentials) {
57040
+ if (!credentials.impersonateServiceAccount || credentials.accessToken) {
57041
+ return undefined;
57042
+ }
57043
+ const sourceAuth = new google_auth_library_1.GoogleAuth({
57044
+ credentials: credentials.credentials && JSON.parse(credentials.credentials),
57045
+ scopes: [CLOUD_PLATFORM_SCOPE],
57046
+ });
57047
+ return createImpersonatedClient(credentials.impersonateServiceAccount, new DeferredAuthClient(() => sourceAuth.getClient()));
57048
+ }
57049
+ function bigQueryClientOptions(credentials, projectId, authClient) {
56991
57050
  const base = {
56992
57051
  projectId,
56993
57052
  scopes: EXTRA_GOOGLE_SCOPES,
56994
57053
  location: credentials.location || undefined,
56995
57054
  };
56996
57055
  if (credentials.accessToken) {
56997
- const authClient = new google_auth_library_1.OAuth2Client();
56998
- authClient.setCredentials({ access_token: credentials.accessToken });
56999
- return Object.assign(Object.assign({}, base), { authClient: authClient });
57056
+ const tokenClient = new google_auth_library_1.OAuth2Client();
57057
+ tokenClient.setCredentials({ access_token: credentials.accessToken });
57058
+ return Object.assign(Object.assign({}, base), { authClient: tokenClient });
57059
+ }
57060
+ if (authClient) {
57061
+ return Object.assign(Object.assign({}, base), { authClient });
57000
57062
  }
57001
57063
  return Object.assign(Object.assign({}, base), { credentials: credentials.credentials ? JSON.parse(credentials.credentials) : undefined });
57002
57064
  }
@@ -57009,10 +57071,11 @@ function requireBigquery () {
57009
57071
  const BIGQUERY_INTERNAL_ERROR_JOB_MAX_ATTEMPTS = 3;
57010
57072
  function createBigQueryClientProvider(credentials) {
57011
57073
  const clients = new Map();
57074
+ const authClient = createAuthClient(credentials);
57012
57075
  return (projectId) => {
57013
57076
  projectId = projectId || credentials.projectId;
57014
57077
  if (!clients.has(projectId)) {
57015
- clients.set(projectId, new bigquery_1.BigQuery(bigQueryClientOptions(credentials, projectId)));
57078
+ clients.set(projectId, new bigquery_1.BigQuery(bigQueryClientOptions(credentials, projectId, authClient)));
57016
57079
  }
57017
57080
  return clients.get(projectId);
57018
57081
  };
@@ -58033,6 +58096,7 @@ function requireBigquery_extract () {
58033
58096
  projectId: spec.billingProject || spec.project,
58034
58097
  location: conn.location,
58035
58098
  accessToken: conn.accessToken,
58099
+ impersonateServiceAccount: conn.impersonateServiceAccount,
58036
58100
  credentials: conn.credentials,
58037
58101
  }));
58038
58102
  const rowCap = (_d = args.rowCap) !== null && _d !== void 0 ? _d : DEFAULT_ROW_CAP;
@@ -64948,25 +65012,22 @@ function requireCredentials () {
64948
65012
  const fs = __importStar(require$$0$5);
64949
65013
  const console_1 = requireConsole();
64950
65014
  const util_1 = requireUtil();
64951
- function getBigQueryCredentials() {
64952
- const locationIndex = (0, console_1.selectionQuestion)("Enter the location of your datasets:", [
65015
+ function getBigQueryCredentials(prompts = { question: console_1.question, selectionQuestion: console_1.selectionQuestion }) {
65016
+ const locationIndex = prompts.selectionQuestion("Enter the location of your datasets:", [
64953
65017
  "US (default)",
64954
65018
  "EU",
64955
65019
  "other",
64956
65020
  ]);
64957
65021
  let location = locationIndex === 0 ? "US" : "EU";
64958
65022
  if (locationIndex === 2) {
64959
- location = (0, console_1.question)("Enter the location's region name (e.g. 'asia-south1'):");
65023
+ location = prompts.question("Enter the location's region name (e.g. 'asia-south1'):");
64960
65024
  }
64961
- const isApplicationDefaultOrJSONKeyIndex = (0, console_1.selectionQuestion)("Do you wish to use Application Default Credentials or JSON Key:", ["ADC (default)", "JSON Key"]);
65025
+ const isApplicationDefaultOrJSONKeyIndex = prompts.selectionQuestion("Do you wish to use Application Default Credentials or JSON Key:", ["ADC (default)", "JSON Key"]);
64962
65026
  if (isApplicationDefaultOrJSONKeyIndex === 0) {
64963
- const projectId = (0, console_1.question)("Enter your billing project ID:");
64964
- return {
64965
- projectId,
64966
- location,
64967
- };
65027
+ const projectId = prompts.question("Enter your billing project ID:");
65028
+ return withServiceAccountToImpersonate({ projectId, location }, prompts);
64968
65029
  }
64969
- const cloudCredentialsPath = (0, util_1.actuallyResolve)((0, console_1.question)("Follow instructions for creating a service account key for BigQuery access\n" +
65030
+ const cloudCredentialsPath = (0, util_1.actuallyResolve)(prompts.question("Follow instructions for creating a service account key for BigQuery access\n" +
64970
65031
  "(see https://cloud.google.com/iam/docs/keys-create-delete) and download as JSON.\n" +
64971
65032
  "(You can delete this file after credential initialization is complete.)\n\n" +
64972
65033
  "Enter the path to your Google Cloud private key file:"));
@@ -64974,11 +65035,17 @@ function requireCredentials () {
64974
65035
  throw new Error(`Google Cloud private key file "${cloudCredentialsPath}" does not exist!`);
64975
65036
  }
64976
65037
  const cloudCredentials = JSON.parse(fs.readFileSync(cloudCredentialsPath, "utf8"));
64977
- return {
65038
+ return withServiceAccountToImpersonate({
64978
65039
  projectId: cloudCredentials.project_id,
64979
65040
  credentials: fs.readFileSync(cloudCredentialsPath, "utf8"),
64980
65041
  location,
64981
- };
65042
+ }, prompts);
65043
+ }
65044
+ function withServiceAccountToImpersonate(credentials, prompts) {
65045
+ const impersonateServiceAccount = prompts
65046
+ .question("Enter a service account to impersonate (optional, press Enter to skip):")
65047
+ .trim();
65048
+ return impersonateServiceAccount ? Object.assign(Object.assign({}, credentials), { impersonateServiceAccount }) : credentials;
64982
65049
  }
64983
65050
 
64984
65051
  return credentials;
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "deepmerge": "^4.2.2",
7
7
  "fs-extra": "^9.0.0",
8
8
  "glob": "13.0.6",
9
- "google-auth-library": "^9.15.1",
9
+ "google-auth-library": "^10.9.1",
10
10
  "google-sql-syntax-ts": "^1.0.3",
11
11
  "js-beautify": "1.15.4",
12
12
  "js-yaml": "^4.3.2",
@@ -90,7 +90,7 @@
90
90
  "engines": {
91
91
  "node": "^20.19.0 || >=22.12.0"
92
92
  },
93
- "version": "1.35.0",
93
+ "version": "1.36.0",
94
94
  "name": "@sqlanvil/cli",
95
95
  "description": "sqlanvil command line interface.",
96
96
  "main": "bundle.js"
package/worker_bundle.js CHANGED
@@ -49746,6 +49746,7 @@ function requireTs () {
49746
49746
  * @property {string|null} [credentials] BigQuery credentials
49747
49747
  * @property {string|null} [location] BigQuery location
49748
49748
  * @property {string|null} [accessToken] BigQuery accessToken
49749
+ * @property {string|null} [impersonateServiceAccount] BigQuery impersonateServiceAccount
49749
49750
  */
49750
49751
 
49751
49752
  /**
@@ -49795,6 +49796,14 @@ function requireTs () {
49795
49796
  */
49796
49797
  BigQuery.prototype.accessToken = "";
49797
49798
 
49799
+ /**
49800
+ * BigQuery impersonateServiceAccount.
49801
+ * @member {string} impersonateServiceAccount
49802
+ * @memberof sqlanvil.BigQuery
49803
+ * @instance
49804
+ */
49805
+ BigQuery.prototype.impersonateServiceAccount = "";
49806
+
49798
49807
  /**
49799
49808
  * Creates a new BigQuery instance using the specified properties.
49800
49809
  * @function create
@@ -49831,6 +49840,8 @@ function requireTs () {
49831
49840
  writer.uint32(/* id 4, wireType 2 =*/34).string(message.location);
49832
49841
  if (message.accessToken != null && Object.hasOwnProperty.call(message, "accessToken"))
49833
49842
  writer.uint32(/* id 5, wireType 2 =*/42).string(message.accessToken);
49843
+ if (message.impersonateServiceAccount != null && Object.hasOwnProperty.call(message, "impersonateServiceAccount"))
49844
+ writer.uint32(/* id 6, wireType 2 =*/50).string(message.impersonateServiceAccount);
49834
49845
  return writer;
49835
49846
  };
49836
49847
 
@@ -49897,6 +49908,10 @@ function requireTs () {
49897
49908
  message.accessToken = reader.string();
49898
49909
  break;
49899
49910
  }
49911
+ case 6: {
49912
+ message.impersonateServiceAccount = reader.string();
49913
+ break;
49914
+ }
49900
49915
  default:
49901
49916
  reader.skipType(tag & 7, long);
49902
49917
  break;
@@ -49953,6 +49968,9 @@ function requireTs () {
49953
49968
  if (message.accessToken != null && Object.hasOwnProperty.call(message, "accessToken"))
49954
49969
  if (!$util.isString(message.accessToken))
49955
49970
  return "accessToken: string expected";
49971
+ if (message.impersonateServiceAccount != null && Object.hasOwnProperty.call(message, "impersonateServiceAccount"))
49972
+ if (!$util.isString(message.impersonateServiceAccount))
49973
+ return "impersonateServiceAccount: string expected";
49956
49974
  return null;
49957
49975
  };
49958
49976
 
@@ -49982,6 +50000,8 @@ function requireTs () {
49982
50000
  message.location = String(object.location);
49983
50001
  if (object.accessToken != null)
49984
50002
  message.accessToken = String(object.accessToken);
50003
+ if (object.impersonateServiceAccount != null)
50004
+ message.impersonateServiceAccount = String(object.impersonateServiceAccount);
49985
50005
  return message;
49986
50006
  };
49987
50007
 
@@ -50007,6 +50027,7 @@ function requireTs () {
50007
50027
  object.credentials = "";
50008
50028
  object.location = "";
50009
50029
  object.accessToken = "";
50030
+ object.impersonateServiceAccount = "";
50010
50031
  }
50011
50032
  if (message.projectId != null && Object.hasOwnProperty.call(message, "projectId"))
50012
50033
  object.projectId = message.projectId;
@@ -50016,6 +50037,8 @@ function requireTs () {
50016
50037
  object.location = message.location;
50017
50038
  if (message.accessToken != null && Object.hasOwnProperty.call(message, "accessToken"))
50018
50039
  object.accessToken = message.accessToken;
50040
+ if (message.impersonateServiceAccount != null && Object.hasOwnProperty.call(message, "impersonateServiceAccount"))
50041
+ object.impersonateServiceAccount = message.impersonateServiceAccount;
50019
50042
  return object;
50020
50043
  };
50021
50044