@malloydata/malloyyo 0.2.33 → 0.2.34

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.
Files changed (2) hide show
  1. package/dist/index.js +57 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -86,6 +86,16 @@ function readSiteConfig(dir) {
86
86
  return analytics ? { analytics } : {};
87
87
  }
88
88
  var normalizeUrl = (u) => u.replace(/\/+$/, "");
89
+ function targetUrl(name, cfg) {
90
+ if (typeof cfg.url !== "string" || cfg.url === "") {
91
+ throw new Error(
92
+ `Target "${name}" has no "url" in malloy-config.json. Add one:
93
+ "malloyyo": { "targets": { "${name}": { "url": "https://<instance>", "dataset": "<dataset>" } } }
94
+ Or name the instance on the command line: malloyyo publish -i https://<instance>`
95
+ );
96
+ }
97
+ return normalizeUrl(cfg.url);
98
+ }
89
99
  function resolveTarget(dir, name) {
90
100
  const targets = readTargetMap(dir);
91
101
  const entries = Object.entries(targets);
@@ -102,7 +112,7 @@ ${TARGETS_BLOCK_EXAMPLE}`
102
112
  const [onlyName, onlyCfg] = entries[0];
103
113
  return {
104
114
  name: onlyName,
105
- url: normalizeUrl(onlyCfg.url),
115
+ url: targetUrl(onlyName, onlyCfg),
106
116
  dataset: onlyCfg.dataset,
107
117
  tokenEnv: onlyCfg.malloyyo_token?.env
108
118
  };
@@ -114,7 +124,7 @@ ${TARGETS_BLOCK_EXAMPLE}`
114
124
  }
115
125
  return {
116
126
  name,
117
- url: normalizeUrl(cfg.url),
127
+ url: targetUrl(name, cfg),
118
128
  dataset: cfg.dataset,
119
129
  tokenEnv: cfg.malloyyo_token?.env
120
130
  };
@@ -132,14 +142,34 @@ function resolveInstance(dir, arg) {
132
142
  const available = entries.map(([n]) => n).join(", ") || "(none defined)";
133
143
  throw new Error(`Unknown target "${arg}". Pass a target name, a URL, or one of: ${available}`);
134
144
  }
135
- return { name: arg, url: normalizeUrl(cfg.url) };
145
+ return { name: arg, url: targetUrl(arg, cfg) };
136
146
  }
137
147
  if (entries.length === 0) throw new Error("No targets defined. Pass a target name or a URL.");
138
- if (entries.length === 1) return { name: entries[0][0], url: normalizeUrl(entries[0][1].url) };
139
- const urls = new Set(entries.map(([, c]) => normalizeUrl(c.url)));
148
+ if (entries.length === 1) return { name: entries[0][0], url: targetUrl(entries[0][0], entries[0][1]) };
149
+ const urls = new Set(entries.map(([n, c]) => targetUrl(n, c)));
140
150
  if (urls.size === 1) return { name: entries.map(([n]) => n).join("/"), url: [...urls][0] };
141
151
  throw new Error(`Multiple targets \u2014 specify which: ${entries.map(([n]) => n).join(", ")} (or a URL).`);
142
152
  }
153
+ function resolvePublishTarget(dir, name, overrides = {}) {
154
+ const { instance, dataset } = overrides;
155
+ if (instance === void 0 && dataset === void 0) return resolveTarget(dir, name);
156
+ if (instance !== void 0 && dataset !== void 0 && name === void 0) {
157
+ const resolved2 = resolveInstance(dir, instance);
158
+ return { name: resolved2.name, url: resolved2.url, dataset };
159
+ }
160
+ const base = resolveTarget(dir, name);
161
+ if (instance === void 0) return { ...base, dataset };
162
+ const resolved = resolveInstance(dir, instance);
163
+ return {
164
+ name: resolved.name,
165
+ url: resolved.url,
166
+ dataset: dataset ?? base.dataset
167
+ // tokenEnv is deliberately NOT carried over. It names a credential for the
168
+ // CONFIGURED instance, and this is a different one — sending that token to another
169
+ // host is both wrong and a leak. Falling through to --token or the stored login for
170
+ // the new URL is the safe default.
171
+ };
172
+ }
143
173
 
144
174
  // src/gather.ts
145
175
  import { readFileSync as readFileSync2, existsSync as existsSync2, readdirSync, statSync } from "node:fs";
@@ -2705,7 +2735,7 @@ function clearCreds(url6) {
2705
2735
  }
2706
2736
 
2707
2737
  // package.json
2708
- var version = "0.2.33";
2738
+ var version = "0.2.34";
2709
2739
 
2710
2740
  // src/http.ts
2711
2741
  var USER_AGENT = `malloyyo/${version}`;
@@ -5201,9 +5231,23 @@ function requestFailed(what, res, out, t, source) {
5201
5231
  const hint = res.status === 401 || res.status === 403 ? authHint(res.status, t, source) : failureHint(out, t);
5202
5232
  return new Error(`${what} failed: ${detail}${hint}`);
5203
5233
  }
5234
+ var PUBLISH_HELP = `Target resolution:
5235
+ The instance and dataset normally come from the \`malloyyo\` block in
5236
+ malloy-config.json. Either can be overridden, and giving BOTH means the
5237
+ config is never read \u2014 so a repo with no targets (or none for this
5238
+ instance) can be published without editing its committed config:
5239
+
5240
+ malloyyo publish -i https://gravity.malloyyo.com --dataset movies --create-dataset
5241
+
5242
+ -i takes what \`login\` takes: a URL, or the name of a configured target
5243
+ whose url should be borrowed. Authenticate the same way as always:
5244
+ \`malloyyo login <url>\`, or pass --token.`;
5204
5245
  async function publish(target, dir, opts) {
5205
5246
  const root = resolve3(dir);
5206
- const t = resolveTarget(root, target);
5247
+ const t = resolvePublishTarget(root, target, {
5248
+ instance: opts.instance,
5249
+ dataset: opts.dataset
5250
+ });
5207
5251
  const source = tokenSource(t, { tokenFlag: opts.token });
5208
5252
  const bearer = await getAccessToken(t, { tokenFlag: opts.token });
5209
5253
  const { files, config } = gatherDirectory(root);
@@ -5282,7 +5326,12 @@ program.command("logout").argument("[target]", "target name or instance URL (opt
5282
5326
  program.command("publish").argument(
5283
5327
  "[target]",
5284
5328
  "named target from the `malloyyo` config block; optional when the repo defines one"
5285
- ).argument("[dir]", "directory to publish", ".").option("--token <token>", "bearer token (overrides login/env)").option("--dry-run", "gather and report what would be sent, but don't POST").option("--skip-lint", "skip the pre-publish dashboard lint").option("--create-dataset", "create the target dataset if it doesn't exist yet (private)").description('push the Malloy model in <dir> (default ".") to <target>').action(publish);
5329
+ ).argument("[dir]", "directory to publish", ".").option(
5330
+ "-i, --instance <instance>",
5331
+ "instance URL, or a configured target name to borrow the url from; overrides the config"
5332
+ ).option("--dataset <dataset>", "dataset to publish into; overrides the config").option("--token <token>", "bearer token (overrides login/env)").option("--dry-run", "gather and report what would be sent, but don't POST").option("--skip-lint", "skip the pre-publish dashboard lint").option("--create-dataset", "create the target dataset if it doesn't exist yet (private)").description('push the Malloy model in <dir> (default ".") to <target>').addHelpText("after", `
5333
+ ${PUBLISH_HELP}
5334
+ `).action(publish);
5286
5335
  program.command("lint").argument("[dir]", "directory to lint", ".").description("validate ./dashboards against the model (manifest, query, givens, Dashboard.tsx)").action(async (dir) => {
5287
5336
  const root = resolve3(dir);
5288
5337
  const report = await lintDashboards(root);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloyyo",
3
- "version": "0.2.33",
3
+ "version": "0.2.34",
4
4
  "description": "Publish Malloy models to a Malloyyo instance",
5
5
  "license": "MIT",
6
6
  "repository": {