@maka/meteor-sdk 0.2.5 → 0.2.6

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 (27) hide show
  1. package/README.md +25 -1
  2. package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/.package-lock.json +1 -1
  3. package/isopackets/combined/npm/node_modules/meteor/boilerplate-generator/node_modules/.package-lock.json +1 -1
  4. package/isopackets/combined/npm/node_modules/meteor/constraint-solver/node_modules/.package-lock.json +1 -1
  5. package/isopackets/combined/npm/node_modules/meteor/ddp-client/node_modules/.package-lock.json +1 -1
  6. package/isopackets/combined/npm/node_modules/meteor/ecmascript/node_modules/.package-lock.json +1 -1
  7. package/isopackets/combined/npm/node_modules/meteor/ecmascript-runtime/node_modules/.package-lock.json +1 -1
  8. package/isopackets/combined/npm/node_modules/meteor/ecmascript-runtime-client/node_modules/.package-lock.json +1 -1
  9. package/isopackets/combined/npm/node_modules/meteor/ecmascript-runtime-server/node_modules/.package-lock.json +1 -1
  10. package/isopackets/combined/npm/node_modules/meteor/fetch/node_modules/.package-lock.json +1 -1
  11. package/isopackets/combined/npm/node_modules/meteor/inter-process-messaging/node_modules/.package-lock.json +1 -1
  12. package/isopackets/combined/npm/node_modules/meteor/logging/node_modules/.package-lock.json +1 -1
  13. package/isopackets/combined/npm/node_modules/meteor/logic-solver/node_modules/.package-lock.json +1 -1
  14. package/isopackets/combined/npm/node_modules/meteor/meteor/node_modules/.package-lock.json +1 -1
  15. package/isopackets/combined/npm/node_modules/meteor/modules/node_modules/.package-lock.json +1 -1
  16. package/isopackets/combined/npm/node_modules/meteor/modules-runtime/node_modules/.package-lock.json +1 -1
  17. package/isopackets/combined/npm/node_modules/meteor/npm-mongo/node_modules/.package-lock.json +1 -1
  18. package/isopackets/combined/npm/node_modules/meteor/package-version-parser/node_modules/.package-lock.json +1 -1
  19. package/isopackets/combined/npm/node_modules/meteor/promise/node_modules/.package-lock.json +1 -1
  20. package/isopackets/combined/npm/node_modules/meteor/react-fast-refresh/node_modules/.package-lock.json +1 -1
  21. package/isopackets/combined/npm/node_modules/meteor/socket-stream-client/node_modules/.package-lock.json +1 -1
  22. package/package.json +1 -1
  23. package/src/console.js +48 -1
  24. package/src/index.js +10 -0
  25. package/src/run.js +11 -3
  26. package/tools/runners/mongo-download.js +3 -0
  27. package/tools/utils/http-helpers.js +68 -15
package/README.md CHANGED
@@ -94,7 +94,11 @@ await handle.stop(); // stops the proxy, then the app; no orphaned processes
94
94
 
95
95
  **No Mongo lifecycle management.** Unlike `meteor run`, `createAppRunner()`
96
96
  does not start or stop a `mongod` for you -- supply a running Mongo's
97
- connection string via `mongoUrl`.
97
+ connection string via `mongoUrl`. For an app that doesn't use the `mongo`
98
+ package at all, pass `mongoUrl: null` explicitly: no `MONGO_URL` is set
99
+ for the app process. (An app that *does* load the `mongo` package will
100
+ crash at startup without one -- the explicit `null` is how you take
101
+ responsibility for that.)
98
102
 
99
103
  There is no separate `stdout`/`stderr` event -- all app log output is
100
104
  observable via `sdk.on("log", ...)` (see below).
@@ -191,6 +195,26 @@ regardless of whether it hangs, or set `METEOR_MODERN='{"watcher":false}'`
191
195
  to use the plain polling watcher instead, which doesn't exhibit either
192
196
  symptom.
193
197
 
198
+ ### `setConsoleOutput(enabled)`
199
+
200
+ By default the tool's Console renders informational output to this
201
+ process's stdout/stderr *and* emits it as `log` events. An embedder
202
+ with its own progress UI (spinners, status lines) can call
203
+ `sdk.setConsoleOutput(false)` to suppress the terminal rendering --
204
+ newline-terminated Console writes landing between spinner redraws
205
+ garble the terminal otherwise -- and paint the emitted events itself.
206
+ Scoping it to a phase (off around a build, back on after) is the
207
+ intended use; events keep flowing either way.
208
+
209
+ ### `setProgressDisplay(enabled)`
210
+
211
+ The tool's own interactive progress bar (the classic `meteor run`
212
+ "Building the application" bar) is disabled by default for embedders.
213
+ `sdk.setProgressDisplay(true)` turns it back on for long build phases --
214
+ it renders only when this process's stdout is a real TTY, and falls back
215
+ to no display otherwise, so it's always safe to call. Turn it off again
216
+ with `false` when your own UI takes over the terminal.
217
+
194
218
  ### Events
195
219
 
196
220
  ```js
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-6k929wsbwx9",
2
+ "name": "package-new-19gk8ik1laok",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-2mclqghmngi",
2
+ "name": "package-new-1cilc0ofj7n",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-icvdrmvpe98",
2
+ "name": "package-new-nmm4d6phk2",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-1l89kpenr14",
2
+ "name": "package-new-1putse4drfa",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-1a111eybcxg",
2
+ "name": "package-new-3yr3ldqleqo",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-4otzch89cvg",
2
+ "name": "package-new-g0p3mjd9xem",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-1j6yd8m9jmr",
2
+ "name": "package-new-6vlz23amok6",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-u5yoxmo7e8",
2
+ "name": "package-new-fjo86g7rsh",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-gl6nw0kh6ad",
2
+ "name": "package-new-do0zmzqfdtv",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-1s0oep6bdbh",
2
+ "name": "package-new-ni9wfpqalta",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-3esnphw257u",
2
+ "name": "package-new-xlb9mzz4j1",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-aalglzldmd",
2
+ "name": "package-new-af7rda3rf3k",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-170bck5w9fh",
2
+ "name": "package-new-fkoxbzqrvrc",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-flkytgvx9c",
2
+ "name": "package-new-cbf3elisryv",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-i7wq86jxilm",
2
+ "name": "package-new-14j0m0s71m4",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-1l34vs5ock9",
2
+ "name": "package-new-wcdgdfqbzqi",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-5gfs3kuobru",
2
+ "name": "package-new-11b302e551qi",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-17orohwcgko",
2
+ "name": "package-new-gddyphm3avb",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-1srjvp9mfq6",
2
+ "name": "package-new-blaoyieb3zd",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "package-new-18nqdduk0bm",
2
+ "name": "package-new-1c3i33z42bi",
3
3
  "lockfileVersion": 3,
4
4
  "requires": true,
5
5
  "packages": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/meteor-sdk",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Programmatic Node API over the Meteor build tool (bundle/run/create/etc.) -- for driving Meteor's build system from plain Node code instead of shelling out to a meteor CLI (there isn't one anymore; see README.md). \"dependencies\" is generated by scripts/write-tool-package-json.js from scripts/dev-bundle-tool-package.js at the monorepo root -- do not hand-edit that field, edit that file and re-run the script instead.",
5
5
  "main": "src/index.js",
6
6
  "exports": {
package/src/console.js CHANGED
@@ -52,6 +52,13 @@ function patch() {
52
52
  }
53
53
  Console[level] = function (...args) {
54
54
  emitter.emit("log", { level, args });
55
+ if (!terminalOutput) {
56
+ // The embedder has claimed the terminal (setConsoleOutput(false)):
57
+ // events still flow above, but the tool's own stdout/stderr
58
+ // rendering is suppressed so it can't collide with the
59
+ // embedder's spinners/progress UI.
60
+ return undefined;
61
+ }
55
62
  return original.apply(Console, args);
56
63
  };
57
64
  });
@@ -60,4 +67,44 @@ function patch() {
60
67
  return emitter;
61
68
  }
62
69
 
63
- module.exports = { setHeadlessEnv, patch, emitter };
70
+ // Whether the tool's Console also renders to this process's
71
+ // stdout/stderr (the default), in addition to emitting "log" events.
72
+ // Embedders that render their own progress UI (spinners, status lines)
73
+ // can turn this off -- newline-terminated Console writes landing between
74
+ // spinner redraws otherwise garble the terminal -- and paint the emitted
75
+ // events however they like. Toggle freely: scoping it to a phase (off
76
+ // around a build, back on afterward) is the intended use.
77
+ let terminalOutput = true;
78
+ function setConsoleOutput(enabled) {
79
+ terminalOutput = !!enabled;
80
+ }
81
+
82
+ // Opt back in to the tool's own interactive progress bar (the classic
83
+ // `meteor run` "Building web.browser [==== ]" display), which patch()
84
+ // disables by default for embedders. Only actually renders a bar when
85
+ // this process's stdout is a real TTY -- otherwise the tool falls back
86
+ // to its no-op display on its own, so this is always safe to call.
87
+ // The ordering below matters: setPretty()/enableProgressDisplay()
88
+ // construct a fresh ProgressDisplay (which re-reads METEOR_HEADLESS,
89
+ // set by setHeadlessEnv()), so setHeadless(false) must come last to
90
+ // propagate onto the newly built display.
91
+ function setProgressDisplay(enabled) {
92
+ const { Console } = require("../tools/console/console.js");
93
+ if (enabled) {
94
+ Console.setPretty(true);
95
+ Console.enableProgressDisplay(true);
96
+ Console.setHeadless(false);
97
+ } else {
98
+ Console.enableProgressDisplay(false);
99
+ Console.setPretty(false);
100
+ Console.setHeadless(true);
101
+ }
102
+ }
103
+
104
+ module.exports = {
105
+ setHeadlessEnv,
106
+ patch,
107
+ emitter,
108
+ setConsoleOutput,
109
+ setProgressDisplay,
110
+ };
package/src/index.js CHANGED
@@ -46,4 +46,14 @@ module.exports = {
46
46
  consoleAdapter.emitter.off(event, listener);
47
47
  return module.exports;
48
48
  },
49
+
50
+ // Suppress (false) or restore (true, the default) the tool Console's
51
+ // own terminal rendering. "log" events keep flowing either way --
52
+ // embedders with their own progress UI turn this off around a phase
53
+ // and paint the events themselves. See README.
54
+ setConsoleOutput: consoleAdapter.setConsoleOutput,
55
+
56
+ // Opt back in to the tool's own interactive progress bar (disabled by
57
+ // default for embedders); renders only when stdout is a TTY. See README.
58
+ setProgressDisplay: consoleAdapter.setProgressDisplay,
49
59
  };
package/src/run.js CHANGED
@@ -27,10 +27,15 @@ async function createAppRunner(project, options) {
27
27
  assertInitialized();
28
28
 
29
29
  options = options || {};
30
- if (!options.mongoUrl) {
30
+ // mongoUrl: null is a deliberate "this app doesn't use the mongo
31
+ // package" (no MONGO_URL is set for the app process); undefined still
32
+ // means the caller forgot.
33
+ if (options.mongoUrl === undefined) {
31
34
  throw new MeteorSdkError(
32
35
  "createAppRunner() requires options.mongoUrl -- meteor-sdk doesn't " +
33
- "manage a mongod process for you (see README.md)"
36
+ "manage a mongod process for you (see README.md). Pass " +
37
+ "mongoUrl: null explicitly for an app that doesn't use the mongo " +
38
+ "package at all."
34
39
  );
35
40
  }
36
41
 
@@ -64,7 +69,10 @@ async function createAppRunner(project, options) {
64
69
  projectContext: project._projectContext,
65
70
  port: appPort,
66
71
  listenHost: options.appHost,
67
- mongoUrl: options.mongoUrl,
72
+ // || undefined: a null mongoUrl must not reach the app's environment
73
+ // (env.MONGO_URL = null would be stringified to "null" by spawn;
74
+ // undefined-valued env keys are dropped instead).
75
+ mongoUrl: options.mongoUrl || undefined,
68
76
  oplogUrl: options.oplogUrl,
69
77
  rootUrl,
70
78
  proxy,
@@ -197,6 +197,9 @@ exports.ensureMongod = async function () {
197
197
  var buffer = await httpHelpers.getUrlWithResuming({
198
198
  url: downloadInfo.url,
199
199
  encoding: null,
200
+ // The GitHub release-asset URL (linux x64) always 302s to a storage
201
+ // host; without this the redirect stub itself would be "extracted".
202
+ followRedirects: true,
200
203
  });
201
204
 
202
205
  files.mkdir_p(files.pathDirname(cacheDir));
@@ -126,9 +126,13 @@ Object.assign(exports, {
126
126
  // - Respects HTTP_PROXY/HTTPS_PROXY, sets a Meteor User-Agent, honors
127
127
  // $CAFILE for a custom CA.
128
128
  //
129
- // - Never follows redirects (fetch redirect: "manual"): redirects
130
- // don't segregate cookies/headers by origin, and callers like the
131
- // OAuth flow in auth.js depend on seeing the 302 themselves.
129
+ // - Never follows redirects by default (fetch redirect: "manual"):
130
+ // redirects don't segregate cookies/headers by origin, and callers
131
+ // like the OAuth flow in auth.js depend on seeing the 302
132
+ // themselves. Credential-less GET downloads (e.g. a GitHub release
133
+ // asset, which always 302s to a storage host) can opt in with
134
+ // followRedirects: true (5 hops) or a hop count; session/auth
135
+ // headers are dropped on any cross-origin hop.
132
136
  //
133
137
  // - Options: url (or a bare url string instead of the object), method,
134
138
  // headers, qs (object appended to the url as a query string),
@@ -281,19 +285,57 @@ Object.assign(exports, {
281
285
  fetchBody = options.body;
282
286
  }
283
287
 
288
+ // Opt-in redirect following -- see the doc block. Only safe for
289
+ // requests whose body (if any) isn't a one-shot stream.
290
+ var maxRedirects = 0;
291
+ if (options.followRedirects === true) {
292
+ maxRedirects = 5;
293
+ } else if (typeof options.followRedirects === "number" &&
294
+ options.followRedirects > 0) {
295
+ maxRedirects = options.followRedirects;
296
+ }
297
+
284
298
  var res;
285
299
  try {
286
- armIdleTimer();
287
- res = await undici.fetch(url, {
288
- method: options.method || 'GET',
289
- headers: headers,
290
- body: fetchBody,
291
- // See the redirect comment in this function's doc block.
292
- redirect: 'manual',
293
- signal: controller.signal,
294
- dispatcher: buildDispatcher(url, undici),
295
- ...(bodyStream ? { duplex: 'half' } : null),
296
- });
300
+ var currentUrl = url;
301
+ var currentMethod = options.method || 'GET';
302
+ var currentHeaders = headers;
303
+ for (var hop = 0; ; hop++) {
304
+ armIdleTimer();
305
+ res = await undici.fetch(currentUrl, {
306
+ method: currentMethod,
307
+ headers: currentHeaders,
308
+ body: fetchBody,
309
+ // See the redirect comment in this function's doc block.
310
+ redirect: 'manual',
311
+ signal: controller.signal,
312
+ dispatcher: buildDispatcher(currentUrl, undici),
313
+ ...(bodyStream ? { duplex: 'half' } : null),
314
+ });
315
+
316
+ var redirectLocation = res.headers.get('location');
317
+ if (hop < maxRedirects && redirectLocation &&
318
+ [301, 302, 303, 307, 308].indexOf(res.status) !== -1) {
319
+ if (res.body) {
320
+ try { await res.body.cancel(); } catch (e) { /* best effort */ }
321
+ }
322
+ var nextUrl = new URL(redirectLocation, currentUrl).href;
323
+ if (new URL(nextUrl).origin !== new URL(url).origin) {
324
+ // Never leak Meteor-services credentials across origins.
325
+ currentHeaders = _.omit(currentHeaders,
326
+ 'X-Meteor-Session', 'X-Meteor-Auth', 'Authorization', 'Cookie');
327
+ }
328
+ if (res.status === 303 ||
329
+ ((res.status === 301 || res.status === 302) &&
330
+ currentMethod !== 'GET' && currentMethod !== 'HEAD')) {
331
+ currentMethod = 'GET';
332
+ fetchBody = undefined;
333
+ }
334
+ currentUrl = nextUrl;
335
+ continue;
336
+ }
337
+ break;
338
+ }
297
339
 
298
340
  // Response shape: the subset of the old request-package response
299
341
  // object our callers actually read.
@@ -310,7 +352,7 @@ Object.assign(exports, {
310
352
  var response = {
311
353
  statusCode: res.status,
312
354
  headers: headerObj,
313
- request: { href: url },
355
+ request: { href: currentUrl },
314
356
  };
315
357
 
316
358
  // Stream the response body: to outputStream when given (body
@@ -493,6 +535,17 @@ Object.assign(exports, {
493
535
  const href = response.request.href;
494
536
  throw Error(`Could not get ${href}; server returned [${response.statusCode}]`);
495
537
  }
538
+ // A download must never silently accept a redirect body as the
539
+ // payload (a GitHub release URL 302s; un-gzipping the redirect stub
540
+ // yields "unexpected end of file"). Callers that expect a redirect
541
+ // should pass followRedirects.
542
+ if (response.statusCode >= 300 && response.statusCode < 400) {
543
+ const href = response.request.href;
544
+ throw Error(
545
+ `Could not get ${href}; server redirected [${response.statusCode}] ` +
546
+ `to ${response.headers.location || "(no Location)"} -- pass ` +
547
+ `followRedirects to follow it`);
548
+ }
496
549
 
497
550
  // Really end the stream if we got this far.
498
551
  outputStream.end(true);