├── .gitignore ├── .gitmodules ├── .nvmrc ├── Contributing.md ├── README.md ├── _config.yml ├── assets ├── api-box.html └── theme-colors.less ├── conversion ├── convert-toc-to-redirects.js └── old-toc.js ├── data └── .gitkeep ├── jsdoc ├── docdata-jsdoc-template │ └── publish.js ├── jsdoc-conf.json └── jsdoc.sh ├── long-form ├── README.md ├── alternate-windows-installation.md ├── appcache.md ├── breaking-changes-1.2.md ├── build-machines.md ├── building-cross-platform-packages.md ├── editing-tools-for-windows-development.md ├── file-change-watcher-efficiency.md ├── oplog-observe-driver.md ├── package-server-api.md └── tracker-manual.md ├── netlify.toml ├── package-lock.json ├── package.json ├── renovate.json ├── scripts ├── api-box.js ├── changelog.js ├── dl.js ├── nameToId.js ├── old-redirects.js └── parseTagOptions.js └── source ├── api ├── accounts-multi.md ├── accounts.md ├── assets.md ├── blaze.md ├── check.md ├── collections.md ├── connections.md ├── core.md ├── ejson.md ├── email.md ├── http.md ├── methods.md ├── mobile-config.md ├── packagejs.md ├── passwords.md ├── pubsub.md ├── reactive-dict.md ├── reactive-var.md ├── session.md ├── templates.md ├── timers.md └── tracker.md ├── changelog.md ├── commandline.md ├── environment-variables.md ├── index.md ├── packages ├── accounts-ui.md ├── appcache.md ├── audit-argument-checks.md ├── autoupdate.md ├── browser-policy.md ├── bundle-visualizer.md ├── coffeescript.md ├── dynamic-import.md ├── ecmascript.md ├── less.md ├── markdown.md ├── modules.md ├── oauth-encryption.md ├── random.md ├── server-render.md ├── spacebars.md ├── underscore.md └── webapp.md └── windows.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | db.json 4 | *.log 5 | node_modules/ 6 | public/ 7 | .deploy*/ 8 | data/ 9 | _multiconfig.yml 10 | .idea/ 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "themes/meteor"] 2 | path = themes/meteor 3 | url = https://github.com/meteor/hexo-theme-meteor.git 4 | [submodule "code"] 5 | path = code 6 | url = https://github.com/meteor/meteor.git 7 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 8.11.2 2 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | ## Contributing to Meteor Documentation 2 | 3 | Thanks for considering contributing to Meteor documentation. We really appreciate your assistance. 4 | 5 | The simplest way to contribute to a project is to open an issue or pull request (PR) on the relevant GitHub issue tracker: 6 | 7 | - Meteor API Docs: https://github.com/meteor/docs/issues 8 | - Meteor Guide: https://github.com/meteor/guide/issues 9 | - Meteor Tutorials : https://github.com/meteor/tutorials/issues 10 | 11 | ## Issues 12 | 13 | If you see a problem with a particular piece of content, you can hit the "Edit on GitHub" button to jump straight to the file on GitHub. If you are keen, you can submit a Pull Request immediately to fix the problem; alternatively, hit the "Issues" button at the top to open an issue reporting the problem. 14 | 15 | ## Pull Requests 16 | 17 | We welcome changes both large and small. 18 | 19 | ### Small changes 20 | 21 | Smaller changes such as fixing typos and small edits for clarity will typically be merged quickly, and are very helpful! Contributions like this enable us to maintain our high quality of documentation 22 | 23 | ### Larger changes 24 | 25 | Larger changes like section rewrites, new sections, and even entirely new articles are also encouraged! In fact, some large parts of our documentation have been contributed by community members. However, more controversial larger changes may require some discussion and buy-in from the documentation maintainers before being accepted—if you are unsure, it may be sensible to open an issue discussing the changes before making them. 26 | 27 | ## Becoming a documentation maintainer 28 | 29 | If you make larger contributions to the above properties, you may be invited to become a community documentation maintainer. This is both a recognition of your contribution (thank you!) and the granting of commit rights on the above repositories. 30 | 31 | With those rights you can merge the Pull Requests of others as well as commit directly to the repository (and in some cases deploy the site). So please use your discretion and be courteous. 32 | 33 | ### Current community maintainers 34 | 35 | - [Loren Sands-Ramshaw](http://github.com/lorensr) 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Meteor API Documentation - http://docs.meteor.com 2 | 3 | This is a [hexo](https://hexo.io) static site used to generate the [Meteor API Docs](http://docs.meteor.com). 4 | 5 | ## Contributing 6 | 7 | We'd love your contributions! Please send us Pull Requests or open issues on [github](https://github.com/meteor/docs). Also, read the [contribution guidelines](https://github.com/meteor/docs/blob/master/Contributing.md). 8 | 9 | If you are making a larger contribution, you may need to run the site locally: 10 | 11 | ### Running locally 12 | 13 | #### Submodules 14 | 15 | This repo has two submodules, one the theme, the other full Meteor repository. 16 | 17 | We have the Meteor repo to generate the `data.js` file (see below). 18 | 19 | After cloning, or updating the repo, it makes sense to run 20 | 21 | ``` 22 | git submodule update --init 23 | ``` 24 | 25 | Generally you should not commit changes to the submodules, unless you know what you are doing. 26 | 27 | #### Generating `data.js` 28 | 29 | To generate the api boxes, the site uses a file `data/data.js` which is generated from the js docs in the [Meteor source code](https://github.com/meteor/meteor). This will automatically happen whenever you start your local hexo server. 30 | 31 | #### Starting hexo 32 | 33 | Ensure you've run `npm install`. Then simply `npm start`. 34 | -------------------------------------------------------------------------------- /assets/api-box.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | {{locus}} 5 |
6 | 7 | <{{hTag}} title="{{{title}}}" class="title-api selflink" id={{id}}> 8 | {{{signature}}} 9 | 10 | 11 |
12 | {{#if importName}} 13 |
import { {{importName}} } from 'meteor/{{module}}'
14 | {{/if}} 15 | 16 | {{#if filepath}} 17 | 18 | ({{filepath}}, line {{lineno}}) 19 | 20 | {{/if}} 21 |
22 |
23 | 24 |
25 |
26 | {{{markdown summary}}} 27 |
28 | 29 | {{#if paramsNoOptions}} 30 |

Arguments

31 |
32 | {{#each paramsNoOptions}} 33 |
34 | {{name}} 35 | {{{typeNames type.names}}} 36 |
37 |
38 | {{{description}}} 39 |
40 | {{/each}} 41 |
42 | {{/if}} 43 | 44 | {{#if options}} 45 |

Options

46 |
47 | {{#each options}} 48 |
49 | {{name}} 50 | {{{typeNames type.names}}} 51 |
52 |
53 | {{{description}}} 54 |
55 | {{/each}} 56 |
57 | {{/if}} 58 | 59 | {{#if UI.contentBlock}} 60 | {{#markdown}}{{> UI.contentBlock}}{{/markdown}} 61 | {{/if}} 62 |
63 |
64 | -------------------------------------------------------------------------------- /assets/theme-colors.less: -------------------------------------------------------------------------------- 1 | // Haven't made any changes as we want the default Meteor colors 2 | -------------------------------------------------------------------------------- /conversion/convert-toc-to-redirects.js: -------------------------------------------------------------------------------- 1 | // This file was used to convert the old docs table of contents 2 | // (a slightly modified version of it in old-toc.js) 3 | // to a mapping of redirects hash -> page it lives in. 4 | // 5 | // I ran this script to generate the redirects.js file at scripts/redirects.js 6 | // 7 | // Keeping it in source control just in case we need to redo it for some reason 8 | 9 | var _ = require('underscore'); 10 | var nameToId = require('../scripts/nameToId.js'); 11 | var idToName = {}; 12 | _.each(nameToId, function(id, name) { 13 | idToName[id] = name; 14 | }); 15 | var oldToc = require('./old-toc.js'); 16 | 17 | // these point to X.html 18 | var idsToPagesWithNoHash = {}; 19 | // these point to X.html#name where name is generated from id below 20 | var idsToPages = {}; 21 | 22 | runList = (dir, as) => { 23 | _.each(as, a => { 24 | var aId; 25 | if (!_.isArray(a) && _.isObject(a)){ 26 | aId = a.id; 27 | a = a.name; 28 | } 29 | 30 | if (_.isString(a)) { 31 | var name = a.toLowerCase(); 32 | currFile = `${dir}/${name}.html`; 33 | idsToPagesWithNoHash[name] = currFile; 34 | if (aId) { 35 | idsToPagesWithNoHash[aId] = currFile; 36 | } 37 | } else { 38 | addIds = (ids) => { 39 | _.each(ids, (id) => { 40 | if (_.isArray(id)) { 41 | addIds(id); 42 | } else { 43 | if (_.isObject(id)) { 44 | if (id.type === 'spacer') { 45 | return; 46 | } 47 | id = id.id || id.name; 48 | } 49 | var ourId = (nameToId[id] || id).toLowerCase(); 50 | idsToPages[ourId] = currFile; 51 | } 52 | }); 53 | } 54 | addIds(a); 55 | } 56 | }); 57 | }; 58 | 59 | runList('api', oldToc[2]); 60 | 61 | _.each(oldToc[4][0], id => { 62 | var name = id.name || id; 63 | idsToPagesWithNoHash[name] = 'packages/' + name + '.html'; 64 | }); 65 | 66 | _.each(oldToc[6][0], id => { 67 | idsToPages[id.replace(/\s|\-|\//g, '')] = 'commandline.html'; 68 | }); 69 | 70 | _.each(_.union(_.keys(idsToPages), _.keys(idsToPagesWithNoHash)), id => { 71 | if (idsToPages[id]) { 72 | var page = idsToPages[id]; 73 | var name = idToName[id] || id; 74 | var nameId = name.replace(/[.#]/g, "-"); 75 | console.log(` /#/full/${id}: '${page}#${nameId}'`); 76 | } else { 77 | var page = idsToPagesWithNoHash[id]; 78 | console.log(` /#/full/${id}: '${page}'`); 79 | } 80 | }); 81 | -------------------------------------------------------------------------------- /data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jankapunkt/docs/478c820bc0fb4ad7d716c4358b8088ac03360ddf/data/.gitkeep -------------------------------------------------------------------------------- /jsdoc/docdata-jsdoc-template/publish.js: -------------------------------------------------------------------------------- 1 | /*global require: true */ 2 | (function () { 3 | 'use strict'; 4 | 5 | // This file receives data from JSDoc via the `publish` exported function, 6 | // and converts it into JSON that is written to a file. 7 | 8 | var fs = require('jsdoc/fs'); 9 | var helper = require('jsdoc/util/templateHelper'); 10 | 11 | var _ = require("underscore"); 12 | var stringify = require("canonical-json"); 13 | 14 | // This is the big map of name -> data that we'll write to a file. 15 | var dataContents = {}; 16 | // List of just the names, which we'll also write to a file. 17 | var names = []; 18 | 19 | /** 20 | * Get a tag dictionary from the tags field on the object, for custom fields 21 | * like package 22 | * @param {JSDocData} data The thing you get in the TaffyDB from JSDoc 23 | * @return {Object} Keys are the parameter names, values are the values. 24 | */ 25 | var getTagDict = function (data) { 26 | var tagDict = {}; 27 | 28 | if (data.tags) { 29 | _.each(data.tags, function (tag) { 30 | tagDict[tag.title] = tag.value; 31 | }); 32 | } 33 | 34 | return tagDict; 35 | }; 36 | 37 | // Fix up a JSDoc entry and add it to `dataContents`. 38 | var addToData = function (entry) { 39 | _.extend(entry, getTagDict(entry)); 40 | 41 | // strip properties we don't want 42 | entry.comment = undefined; 43 | entry.___id = undefined; 44 | entry.___s = undefined; 45 | entry.tags = undefined; 46 | 47 | // generate `.filepath` and `.lineno` from `.meta` 48 | if (entry.meta && entry.meta.path) { 49 | var packagesFolder = 'packages/'; 50 | var index = entry.meta.path.indexOf(packagesFolder); 51 | if (index != -1) { 52 | var fullFilePath = entry.meta.path.substr(index + packagesFolder.length) + '/' + entry.meta.filename; 53 | entry.filepath = fullFilePath; 54 | entry.lineno = entry.meta.lineno; 55 | } 56 | } 57 | 58 | entry.meta = undefined; 59 | 60 | if (!entry.importfrompackage && entry.filepath) { 61 | entry.module = entry.filepath.split('/')[0]; 62 | } else { 63 | entry.module = entry.importfrompackage; 64 | } 65 | 66 | names.push(entry.longname); 67 | dataContents[entry.longname] = entry; 68 | }; 69 | 70 | /** 71 | Entry point where JSDoc calls us. It passes us data in the form of 72 | a TaffyDB object (which is an in-JS database of sorts that you can 73 | query for records. 74 | 75 | @param {TAFFY} taffyData See . 76 | @param {object} opts 77 | @param {Tutorial} tutorials 78 | */ 79 | exports.publish = function(taffyData) { 80 | var data = helper.prune(taffyData); 81 | 82 | var namespaces = helper.find(data, {kind: "namespace"}); 83 | 84 | // prepare all of the namespaces 85 | _.each(namespaces, function (namespace) { 86 | if (namespace.summary) { 87 | addToData(namespace); 88 | } 89 | }); 90 | 91 | var properties = helper.find(data, {kind: "member"}); 92 | 93 | _.each(properties, function (property) { 94 | if (property.summary) { 95 | addToData(property); 96 | } 97 | }); 98 | 99 | // Callback descriptions are going to be embeded into Function descriptions 100 | // when they are used as arguments, so we always attach them to reference 101 | // them later. 102 | var callbacks = helper.find(data, {kind: "typedef"}); 103 | _.each(callbacks, function (cb) { 104 | delete cb.comment; 105 | addToData(cb); 106 | }); 107 | 108 | var functions = helper.find(data, {kind: "function"}); 109 | var constructors = helper.find(data, {kind: "class"}); 110 | 111 | // we want to do all of the same transformations to classes and functions 112 | functions = functions.concat(constructors); 113 | 114 | // insert all of the function data into the namespaces 115 | _.each(functions, function (func) { 116 | if (! func.summary) { 117 | // we use the @summary tag to indicate that an item is documented 118 | return; 119 | } 120 | 121 | func.options = []; 122 | var filteredParams = []; 123 | 124 | // Starting a param with `options.` makes it an option, not a 125 | // param. Dot (`.`) in this case binds tighter than comma, so 126 | // `options.foo,bar` will create an option named `foo, bar` 127 | // (representing two options in the docs). We process pipes so 128 | // that `options.foo|bar` also results in `foo, bar`. 129 | _.each(func.params, function (param) { 130 | param.name = param.name.replace(/,|\|/g, ", "); 131 | 132 | var splitName = param.name.split("."); 133 | 134 | if (splitName.length < 2 || splitName[0] !== "options") { 135 | // not an option 136 | filteredParams.push(param); 137 | return; 138 | } 139 | 140 | param.name = splitName[1]; 141 | 142 | func.options.push(param); 143 | }); 144 | 145 | func.params = filteredParams; 146 | 147 | // the entire unparsed doc comment. takes up too much room in the 148 | // data file. 149 | delete func.comment; 150 | 151 | addToData(func); 152 | }); 153 | 154 | // write full docs JSON 155 | var jsonString = stringify(dataContents, null, 2); 156 | var jsString = "module.exports = " + jsonString + ";"; 157 | jsString = "// This file is automatically generated by JSDoc; regenerate it with scripts/admin/jsdoc/jsdoc.sh\n" + jsString; 158 | var docsDataFilename = "../data/data.js"; 159 | fs.writeFileSync(docsDataFilename, jsString); 160 | 161 | // write name tree JSON 162 | jsonString = stringify(names.sort(), null, 2); 163 | var nameTreeFilename= "../data/names.json"; 164 | fs.writeFileSync(nameTreeFilename, jsonString); 165 | }; 166 | })(); 167 | -------------------------------------------------------------------------------- /jsdoc/jsdoc-conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["plugins/markdown"], 3 | "markdown": { 4 | "parser": "gfm" 5 | }, 6 | "source": { 7 | "exclude": [ 8 | "packages/ddp/sockjs-0.3.4.js", 9 | "packages/test-in-browser/diff_match_patch_uncompressed.js", 10 | "packages/jquery/jquery.js", 11 | "packages/underscore/underscore.js", 12 | "packages/json/json2.js", 13 | "packages/minimongo/minimongo_tests.js", 14 | "tools/node_modules", 15 | "tools/skel-pack/package.js" 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /jsdoc/jsdoc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | INFINITY=10000 4 | 5 | TOPDIR=$(pwd) 6 | METEOR_DIR="./code" 7 | cd "$METEOR_DIR" 8 | 9 | # Ensure that jsdoc failure actually makes this script fail. 10 | set -o pipefail 11 | 12 | # Call git grep to find all js files with the appropriate comment tags, 13 | # and only then pass it to JSDoc which will parse the JS files. 14 | # This is a whole lot faster than calling JSDoc recursively. 15 | git grep -ialE "@(summary|borrows|namespace|memberof|alias)" | xargs -L ${INFINITY} -t \ 16 | "$TOPDIR/node_modules/.bin/jsdoc" \ 17 | -t "$TOPDIR/jsdoc/docdata-jsdoc-template" \ 18 | -c "$TOPDIR/jsdoc/jsdoc-conf.json" \ 19 | 2>&1 | grep -v 'WARNING: JSDoc does not currently handle' 20 | -------------------------------------------------------------------------------- /long-form/README.md: -------------------------------------------------------------------------------- 1 | ## Long form documentation 2 | 3 | This is documentation that is too long and detailed to belong in the [Docs](https://docs.meteor.com) or [Guide](https://guide.meteor.com). 4 | 5 | You can view it online at https://github.com/meteor/docs/tree/master/long-form 6 | -------------------------------------------------------------------------------- /long-form/alternate-windows-installation.md: -------------------------------------------------------------------------------- 1 | On some network or machine setups Meteor's installer for Windows may fail. In such cases, follow these simple steps to install Meteor manually: 2 | 3 | 1. Install [7-Zip](http://www.7-zip.org/) or any other program that knows how to extract `tar.gz` files. 4 | 2. Download the installation archive: 5 | * 64-bit: https://packages.meteor.com/bootstrap-link?arch=os.windows.x86_64 6 | * 32-bit: https://packages.meteor.com/bootstrap-link?arch=os.windows.x86_32 7 | 3. In a command prompt, run `echo %LocalAppData%\.meteor` -- this is the directory in which Meteor _must_ be installed. 8 | 4. Extract the installation archive (from step 2) into the directory above. 9 | 5. Add the full directory path from step 3 to your `PATH` environment variable. _([Instructions](https://www.java.com/en/download/help/path.xml))_ 10 | 6. You should now be able to open a new command prompt and run `meteor`. 11 | > _Note:_ Some versions of Windows may require restarting after updating the `PATH`. 12 | -------------------------------------------------------------------------------- /long-form/appcache.md: -------------------------------------------------------------------------------- 1 | This document contains background information about the browser 2 | application cache and how it interacts with Meteor. For details on 3 | the `appcache` package API see http://docs.meteor.com/#appcache 4 | 5 | 6 | ## How the Browser Uses the App Cache 7 | 8 | A key to understanding how the browser uses the application cache is 9 | this: 10 | 11 | *The browser always loads the app cache in the background.* 12 | 13 | Or, to put it another another way, the browser never waits for the app 14 | cache to be updated. 15 | 16 | For example, consider what happens when a user navigates to the app's 17 | web page for the first time, when they don't have the application 18 | cached yet. The browser will load the app as if it were a standard 19 | online application not using an app cache; and then the browser will 20 | also populate the app cache in the background. The *second* time the 21 | user opens the web page the browser load the app from the cache. Why? 22 | Because the browser never waits for the app cache to loaded. The 23 | first time the user opens the page the cache hasn't been loaded yet, 24 | and so the browser loads the page incrementally from the server as it 25 | does for web pages that aren't cache enabled. 26 | 27 | As another example, suppose the user has previously opened the web 28 | page and so has an old copy of the application in the app cache. The 29 | user now returns to the web page, and in the meantime a new version of 30 | the application has been published. What happens? Since the browser 31 | never waits for the app cache, it will at first display the old 32 | version of the web page. Then, as Meteor makes its livedata 33 | connection and sees that a code update is available, the page will 34 | reload with the new code. 35 | 36 | This behavior may seem strange. Why not check first to see if new 37 | code is available and avoid potentially briefly displaying an old 38 | version of the app? But consider if the user is offline, or has a bad 39 | or intermittent Internet connection. We don't know *how long* it will 40 | take to discover that a new version of the app is available. It could 41 | be five seconds or a minute or an hour... depending on when the 42 | browser is able to connect. So rather than waiting, not knowing how 43 | long the wait may be, instead the browser enables using the 44 | application offline by loading the application from the cache, and 45 | then updates the cache when a new version is available and can be 46 | downloaded. 47 | 48 | 49 | ## The App Cache and Meteor Code Reloads 50 | 51 | The appcache package is designed to support Meteor's hot code reload 52 | feature. (If you see any problems with code reloads when using the 53 | app cache, please report it as a bug). 54 | 55 | With the appcache package installed a code reload will follow these 56 | steps: 57 | 58 | * Meteor's livedata stream connection notices that a code update is 59 | available and initiates a reload. 60 | 61 | * The appcache package is notified that a code migration has started 62 | (as the appcache package plugs into Meteor's reload `onMigrate` 63 | hook). The appcache package calls 64 | `window.applicationCache.update()` to ask the browser to update the 65 | app cache. The appcache package then reports back to reload that it 66 | isn't ready for migration yet... until the browser reports that the 67 | app cache has finished updating. The reload is thus delayed until 68 | the new code is in the cache. 69 | 70 | * Meteor's reload calls `window.location.reload()`, which reloads 71 | the app in the web page with the new code in the app cache. 72 | 73 | 74 | ## Designed for Static Resources Only 75 | 76 | When a browser displays an image in an application, we can describe 77 | that image as either being static (it stays the same for any 78 | particular version of the application) or dynamic (it can change as 79 | the application is being used). 80 | 81 | For example, a "todo" app might display a green checkmark image for 82 | completed todo items. This would be a static image, because it is 83 | part of the application and changing the image would require a code 84 | push. 85 | 86 | Conversely, we might imagine that the app could allow the user to 87 | upload images to add to a todo item's description. These images would 88 | be dynamic images, because new images can be added and images can be 89 | images as the application is running. 90 | 91 | The appcache package is only designed to cache static resources. As 92 | an "application" cache, it caches the resources needed by the 93 | application, including the HTML, CSS, Javascript and files published 94 | in the public/ directory. 95 | -------------------------------------------------------------------------------- /long-form/breaking-changes-1.2.md: -------------------------------------------------------------------------------- 1 | We've tried to make upgrading your app or package as painless as possible, but in some cases, you will have to make small modifications to your Meteor 1.1 code to get it to run in Meteor 1.2. This document outlines the breaking changes and potential compatibility issues in Meteor 1.2. 2 | 3 | This document is meant to be as short as possible so that you can read it all. If you want to learn about all of the new features in Meteor 1.2, read here: XXX to be posted later 4 | 5 | ## Build tool and package improvements 6 | 7 | 1. **standard-minifiers package (automatic upgrade):** Starting in Meteor 1.2, your app must include the `standard-minifiers` package to get the default CSS and JS handling that was hard-coded in Meteor 1.1. When you update your app to 1.2 or a prerelease of 1.2, Meteor will add the `standard-minifiers` package to your app automatically. Besides minifying your code in production, the biggest effect of `standard-minifiers` is in how URLs in CSS are interpreted, such as for fonts and images. In a Meteor 1.1 app or an app with `standard-minifiers`, the URLs are interpreted relative to the top level, because all CSS is concatenated in development, and concatenated and minified in production. If you remove `standard-minifiers` in Meteor 1.2, URLs in CSS will be interpreted as relative to the CSS file, as per web standards. This change was made to give developers more control over minification, with less hard-coded functionality. 8 | 2. **PackageAPI#addAssets (change required to package.js file):** In packages, files added with `api.addFiles` that have no recognizable extension are no longer added as static assets. You must now use `api.addAssets` to add a static asset. Package authors must make this change to their `package.js` files before they can publish from Meteor 1.2, but existing published packages will continue to work. The hidden `isAsset: true` flag to `api.addFiles` is now deprecated and throws an error suggesting that you should use `addAssets` instead. 9 | 3. **LESS, CoffeeScript, Stylus incompatible (change to package dependencies and republish required):** We've rewritten the core build plugin packages - LESS, CoffeeScript, and Stylus - to use a new, faster build system. Packages that use these packages will need to update their dependencies to the new versions and be republished before they can be used with Meteor 1.2. 10 | 11 | ## Splitting up meteor-platform 12 | 13 | 1. **New apps don't include meteor-platform (packages need to declare all of their dependencies):** In the past, if a package author forgot to include a dependency on `jquery`, `mongo`, or a similarly common package in your `package.js` file, the package would usually still work because most apps had these packages by default. We are now trying to make these parts of Meteor more modular, so package authors should take care to declare all of their package dependencies in `package.js`. 14 | 2. **test-packages no longer includes any packages globally (no changes needed):** To help with the above transition, we have update the `test-packages` command to avoid making any packages available globally. If your tests passed before but fail in Meteor 1.2 with a message like `$ is undefined`, that means that your package had an undeclared dependency on jQuery and the tests only passed because `test-packages` itself added jQuery to the global namespace. The fix is to make sure your dependencies in `package.js` are correct. 15 | 16 | ## Ecmascript 17 | 18 | 1. **New language keywords (changes required to some variable names):** According to the ES2015 standard, some names that used to be valid have become keywords, for example `package` and `let`. You should get a nice error that will notify you that some names need to be changed. 19 | 20 | ## Case insensitive usernames and emails 21 | 22 | 1. **Meteor will attempt to keep usernames and emails unique (change required to code that reads and writes user data):** In Meteor 1.2, the accounts-password package tries to make sure that users don't need to remember the capitalization of their usernames and email addresses. Some code in your app might be using `Meteor.users.findOne({username: x})` or similar to search for users, and this will be a case-sensitive lookup. Instead, the new API method should be used like so: `Accounts.findUserByUsername(x)` This way, the username will be matched in a case-insensitive fashion. The same thing applies for email addresses and the new API method `Accounts.findUserByEmail`. 23 | 24 | ## React 25 | 26 | 1. **New official React package (update package dependencies if you are using a different React package):** There are several popular React packages for Meteor, and we hope to centralize them all into one package, simply called `react`. If your package depends on `reactjs:react` or another package that loads React, switch to the official one so that users can have one global version of the React library. If you are using the `getMeteorData` mixin for reactive data, you should depend on `react`; if you just need the React rendering library itself, then you should depend on `react-runtime`, which is a subset of the `react` package. 27 | 28 | ## Mobile 29 | 30 | 1. **Bring your own Android tools**: The bundled Android tools have been removed and a system-wide install of the Android SDK is now required. This should make it easier to keep the development toolchain up to date and helps avoid some difficult to diagnose failures. If you don't have your own Android tools installed already, you can find more information about installing the Android SDK for [Mac] (https://github.com/meteor/meteor/wiki/Mobile-Development-Install:-Android-on-Mac) or [Linux] (https://github.com/meteor/meteor/wiki/Mobile-Development-Install:-Android-on-Linux). 31 | 2. **Cordova has been updated from 4.2.0 to 5.2.0**: tools, platforms and plugins have been updated. This may require you to make changes to your app. For details, see the [Cordova release notes](https://cordova.apache.org/#news) for the releases up to 5.2.0. 32 | 2. **New names for Cordova plugins**: We've upgraded to a new version of Cordova that uses npm to manage its plugins instead of the old Cordova registry. As part of this move, many Cordova plugins have been renamed. Meteor should perform conversions automatically, but you may want to be aware of this to avoid surprises. See [here](https://cordova.apache.org/announcements/2015/04/21/plugins-release-and-move-to-npm.html) for more information. 33 | 3. **Output logging**: As a known issue, we do not show logging output when running on the iOS Simulator. As a workaround, you can use `meteor run ios-device` to open the project in Xcode and watch the console output in the debug area. 34 | -------------------------------------------------------------------------------- /long-form/build-machines.md: -------------------------------------------------------------------------------- 1 | Meteor distributes fully pre-build packages called [Isopacks](https://www.meteor.com/isobuild). This helps ensure similar behavior across platforms and means that casual Meteor users do not need to install Xcode in order to run their apps. This means that packages must be built for all platforms that are supported by Meteor (os.osx.x86_64, os.linux.x86_64 and os.linux.x86_32). In the future, this will be done automatically by Meteor -- but for now, we ask that you publish the builds manually. 2 | 3 | In order to help with that, Meteor provides access to pre-configured build machines. To access a build machine, log in with your meteor developer account and run: 4 | 5 | ```sh 6 | # OS X 7 | meteor admin get-machine os.osx.x86_64 8 | 9 | # Linux on 64-bit Intel 10 | meteor admin get-machine os.linux.x86_64 11 | 12 | # Linux on 32-bit Intel 13 | meteor admin get-machine os.linux.x86_32 14 | 15 | # Windows on 32-bit Intel 16 | meteor admin get-machine os.windows.x86_32 17 | ``` 18 | 19 | This will open a secure shell to build machines on all three platforms. From there, you can use ` meteor publish-for-arch` to publish the package; see `meteor help publish-for-arch` for more details. 20 | 21 | For more on Meteor Build, see https://www.meteor.com/services/build. 22 | 23 | # FAQ 24 | 25 | ## Why does Meteor think that my package is not platform-agnostic? 26 | 27 | Your package probably contains binary code. Most likely, this is because: 28 | 29 | * Your package includes a binary NPM module. If your package is in active development, one pattern is to move the NPM dependencies to a different package and version them separately. (To use the core packages as an example, we version `npm-bcrypt` separately from the `accounts` packages, so when we change accounts code, the package we only have to publish a new version of the js-only package) 30 | 31 | * Your package includes a build plugin that uses binary code in some way. A build plugin extends the build process for apps and packages that use this package. Because of this, it is compiled together with its dependencies. So, having a build plugin depend on `npm-bcrypt` will pull in the binary contents of `npm-bcrypt`, and cause the build plugin (and the package containing the build plugin) to contain binary code. 32 | 33 | ## Why is it important to use pre-configured build machines? Why shouldn't I just publish from my laptop? 34 | 35 | Meteor, and, by extension, your package, has many users all around the world. Many of them are running older version of operating systems, using older compilers, etc. These are usually backwards, but not forwards compatible, so a package compiled on a new machine, might throw errors on old machines. 36 | 37 | As an example, here is a github issue that was caused by compiling a package on a machine with the newer version of glibc (https://github.com/meteor/meteor/issues/2554#issuecomment-55264224) We have set up build machines to both avoid this sort of thing, and be able to take responsibility for them if they happen again. 38 | 39 | ## Can you give me more details on get-machine? What are the limits? 40 | 41 | Here is how `meteor admin get-machine` currently works on the client: 42 | * You ask for a machine. 43 | * The server assigns you a machine for a specified (5 minutes default) length of time. 44 | * The server passes you login information. 45 | * If, within that length of time, you run the command again, you will be assigned the same machine. 46 | 47 | Here is how it works on the server: 48 | * There is a set amount of machines/VMs that have mostly been spun up and ready to go. 49 | * When a user asks for one, one is assigned to them for a length of time. 50 | * Once you are assigned a machine, no other user may have it. When the server decides that you are done, so is the machine. 51 | * Sometimes, for technical/economics reasons, it doesn't make much sense to kill the machine immediately. So, you might have requested a machine for five minutes, but get to keep it for up to an hour (and keep getting the same machine). That's cool, but you should not rely on that behavior. 52 | 53 | Here are some time limits: 54 | * You cannot reserve a machine for more than 15 minutes at a time. 55 | * You cannot have more than 60 minutes of machine-usage per week. 56 | 57 | We are still experimenting with these numbers. If you are a package author and you need more time, please let us know what we can do. 58 | -------------------------------------------------------------------------------- /long-form/building-cross-platform-packages.md: -------------------------------------------------------------------------------- 1 | While working on making Meteor compatible with Windows in addition to Mac and Linux, we have come across some advice to help make your package compatible with all operating systems. 2 | 3 | 1. **Setting up Git line endings** - If you want your package to build with the same result on Mac, Linux, and Windows, you should set your Git to not convert to Windows line endings on pull by setting the config option core.autoclrf to input or false. 4 | 2. **Build plugin input paths** - In build plugins, you will get an input path that has the operating system format, meaning it will have backslashes on Windows. Beware that this means that your package might end up with different contents if you use this input path in the output of your build. We have fixed this issue in the most common case of source maps, but you might need to think about it in some cases - for example, if you display the file path of input files in error messages. 5 | -------------------------------------------------------------------------------- /long-form/editing-tools-for-windows-development.md: -------------------------------------------------------------------------------- 1 | While working on the Windows port of Meteor, we came across a few simple tools that made programming on Windows more enjoyable for us. They might be particularly helpful to people coming from a Mac or Linux background where command-line development is more common. 2 | 3 | ## ConEmu 4 | 5 | 6 | 7 | ![Screenshot of ConEmu with PowerShell](http://i.imgur.com/Nfvdweu.png) 8 | 9 | This is a tool that makes using CMD and PowerShell more enjoyable by allowing you to have tabs, easily copy and paste into and out of the window, configure key bindings, set a nicer font, and lots of other nice features. 10 | 11 | ## Notepad++ 12 | 13 | 14 | 15 | ![Gif of notepad++](http://notepad-plus-plus.org/assets/images/notepad4ever.gif) 16 | 17 | Notepad++ is a free text editor for Windows that improves in every single way over its namesake Notepad. It's great for editing any kind of code. 18 | 19 | ## Sublime Text 20 | 21 | 22 | 23 | ![Screenshot of Sublime Text](http://i.imgur.com/xQc65Pi.png) 24 | 25 | Sublime Text is a nice text editor that gets out of your way at the right times and has lots of nice plugins for JavaScript programming. It's not free, but has an unlimited trial period. 26 | 27 | ## Version Control 28 | 29 | You might find it useful to use Git or a different version control system to collaborate on your projects. There are many different Windows-based version control tools available, like [msysgit](https://msysgit.github.io/) (Git, command line) and [GitHub for Windows](https://windows.github.com/) (Git, GUI). 30 | -------------------------------------------------------------------------------- /long-form/file-change-watcher-efficiency.md: -------------------------------------------------------------------------------- 1 | # Overview: How Meteor watches your files for changes 2 | 3 | When you run your Meteor app in local development mode, the `meteor` command-line tool watches your source code for changes. (This page describes the behavior as of Meteor 1.0.2.1.) 4 | 5 | There are two different strategies that `meteor` uses for change watching: efficient system-specific APIs like `inotify` (on Linux) and `kqueue` (on OSX), and a slower but more reliable "stat polling" method. `inotify`/`kqueue` is less reliable because it does not work on every system, and sometimes `meteor` can't even tell if it doesn't work. Stat polling is slower in two ways: it only looks for changes once every "polling interval", and the actual check that it does consumes more CPU. 6 | 7 | By default, `meteor` uses both `inotify`/`kqueue` and stat polling, with a polling interval of 5 seconds; if it detects that `inotify`/`kqueue` isn't working, it uses a polling interval of 0.5 seconds. 8 | 9 | This means that on most systems, changes are detected very quickly by `inotify`/`kqueue`. On systems where `inotify`/`kqueue` doesn't work, the stat polling fallback still works, but it does use more CPU than if `inotify`/`kqueue` works; and if `meteor` was unable to automatically detect that `inotify`/`kqueue` didn't work, you may have to wait up to 5 seconds for stat polling to notice file changes. 10 | 11 | The rest of this wiki page describes two ways in which you can improve the performance of Meteor file watching. 12 | 13 | * There are some circumstances where `inotify` doesn't work (causing `meteor` to fall back to the CPU-hungry 0.5 second poll), but a simple configuration change will make `inotify` work. 14 | * There are some circumstances where `inotify`/`kqueue` doesn't work but `meteor` is unable to detect this, so only the high-latency 5 second poll happens. While you can't make `inotify`/`kqueue` work in this case, you can at least tell `meteor` to use a faster poll to detect changes sooner. 15 | 16 | # Making `inotify` work on Linux systems where it doesn't work 17 | 18 | (Note: this section is Linux-specific: it will not work on OS X, and the warning message below should not be displayed there. If you do see this warning on OS X, please [file a bug](https://github.com/meteor/meteor/wiki/Contributing-to-Meteor#reporting-a-bug-in-meteor)!) 19 | 20 | Linux users may see a message saying: 21 | 22 | ``` 23 | => It looks like a simple tweak to your system's configuration will make many 24 | tools (including this Meteor command) more efficient. To learn more, see 25 | https://github.com/meteor/meteor/wiki/File-Change-Watcher-Efficiency 26 | ``` 27 | 28 | This message means that `meteor` tried to use the Linux `inotify` API to watch for file changes, but you've already reached the system-wide limit for the number of files that you can watch (probably in a different program entirely!). 29 | 30 | Fortunately, this is easy to fix. Just run this command at your shell, entering your password when prompted: 31 | 32 | ``` 33 | echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p 34 | ``` 35 | 36 | (Alternatively, use another mechanism to edit `/etc/sysctl.conf` as root and add the line `fs.inotify.max_user_watches=524288` to the bottom, then run `sudo sysctl -p`.) 37 | 38 | After running these commands, you should no longer see the "simple tweak" message when running `meteor`. (If you still do, [file a bug](https://github.com/meteor/meteor/wiki/Contributing-to-Meteor#reporting-a-bug-in-meteor)!) Meteor is now using the efficient `inotify` API 39 | 40 | If you do not have superuser access on your computer, you won't be able to run these commands. In that case, you may set the `METEOR_WATCH_FORCE_POLLING` environment variable as described in the next section, which will hide the warning and not try to use `inotify` at all. 41 | 42 | # Running `meteor` on a network/shared filesystem 43 | 44 | Some filesystems don't support `inotify`/`kqueue` at all, but also don't allow `meteor` to detect this error case. In this situation, `meteor` will still detect file changes with the stat poller at the 5 second polling interval, but you may want to poll more often in order to detect changes faster. 45 | 46 | The most common filesystems with this issue are network or shared filesystems like NFS or VirtualBox shared folders (vboxsf). For example, if you are using Vagrant or a similar tool to run `meteor` inside a virtual machine, and you are sharing your app source directory between your host machine and the VM, `inotify` may be unable to detect changes made on your host machine. The symptom will be that `meteor` doesn't even start trying to rebuild your app until up to 5 seconds after you make the change. 47 | 48 | To improve this situation, on the machine where you are running `meteor` (eg, inside your VM), set the `METEOR_WATCH_FORCE_POLLING` environment variable to any non-empty value. For example, put the line `METEOR_WATCH_FORCE_POLLING=true` into the `.bashrc` file in your home directory. 49 | 50 | If you would like finer control over the polling interval (to put it somewhere between the options of 5 seconds and 500 ms), set the `METEOR_WATCH_POLLING_INTERVAL_MS` environment variable to a number. 51 | -------------------------------------------------------------------------------- /long-form/oplog-observe-driver.md: -------------------------------------------------------------------------------- 1 | March 18, 2014 2 | 3 | In Meteor 0.7, we reworked how Meteor gets real-time database updates from MongoDB. 4 | 5 | The key method that Meteor adds to the MongoDB API which makes it into a real-time database API is [`cursor.observeChanges`](http://docs.meteor.com/#observe_changes). `observeChanges` run an arbitrary MongoDB query, returns its results via `added` callbacks, and then **continues to watch the query** and calls additional callbacks as its results change. 6 | 7 | Most users don't use `observeChanges` directly, but whenever you return a cursor from a [publish function](http://docs.meteor.com/#meteor_publish), Meteor calls `observeChanges` on that cursor and provides it with callbacks which publish the query's changes to clients. 8 | 9 | Previous versions of Meteor only had one strategy for implementing `observeChanges`: the "poll-and-diff" algorithm, implemented by the `PollingObserveDriver` class. This approach re-runs the query frequently and calculates the difference between each set of results. This code is simple and has historically contained very few bugs. But the cost of the `PollingObserveDriver` is proportional to the poll frequency and to the size of each query result, and the time from database change to callback invocation depends on whether the write originated in the same Meteor server process (very fast) or in another process (up to 10 seconds). 10 | 11 | Starting with Meteor 0.7.0, Meteor can use an additional strategy to implemnt `observeChanges`: **oplog tailing**, implemented by the `OplogObserveDriver` class. 12 | Meteor now knows how to read the MongoDB "operations log" --- a special collection that records all the write operations as they are applied to your database. This means changes to the database can be instantly noticed and reflected in Meteor, whether they originated from Meteor or from an external database client. Oplog tailing has different performance characteristics than "poll-and-diff" which are superior in many cases. 13 | 14 | `OplogObserveDriver` needs to understand the meaning of MongoDB [selectors](http://docs.meteor.com/#selectors), [field specifiers](http://docs.meteor.com/#fieldspecifiers), [modifiers](http://docs.meteor.com/#modifiers), and [sort specifiers](http://docs.meteor.com/#sortspecifiers) at a much deeper level than `PollingObserveDriver`. This is because it actually needs to understand how write operations that it sees in the oplog interact with queries, instead of just relying on the MongoDB server to repeatedly execute the query. To deal with these structures, `OplogObserveDriver` uses Meteor's implementation of the MongoDB query engine, [Minimongo](https://github.com/meteor/meteor/tree/master/packages/minimongo), which Meteor also uses as its client-side local database cache. 15 | 16 | As of Meteor 0.7.2, we use `OplogObserveDriver` for most queries. There are a few types of queries that still use `PollingObserveDriver`: 17 | * [Selectors](http://docs.meteor.com/#selectors) containing geospatial operators, the `$where` operator, or any operator not supported by Minimongo (such as `$text`) 18 | * Queries specifying the `skip` option 19 | * Queries specifying the `limit` option without a `sort` specifier or with a sort based on `$natural` order 20 | * [Field specifiers](http://docs.meteor.com/#fieldspecifiers) and [sort specifiers](http://docs.meteor.com/#sortspecifiers) with `$` operators such as `$slice` or `$natural` 21 | * Calls to `observeChanges` using "ordered" callbacks `addedBefore` and `movedBefore`. (The implicit call to `observeChanges` which occurs when you return a cursor from a [publish function](http://docs.meteor.com/#meteor_publish) does not use the ordered callbacks.) 22 | 23 | Oplog tailing is automatically enabled in development mode with `meteor run`, and can be enabled in production with the `MONGO_OPLOG_URL` environment variable. 24 | 25 | 26 | ### `OplogObserveDriver` in production 27 | 28 | To use oplog tailing in your production Meteor app, your MongoDB servers must be configured as a [replica set](http://docs.mongodb.org/manual/replication/); a single-`mongod` database has no oplog. Your cluster may not use Mongo sharding. 29 | 30 | Once you have set up your replica set, you need to provide read access to the oplog to a user; we recommend creating a special user (say, `oplogger`) that is used only for this purpose. Note that the oplog is shared between **all databases** served by your replica set of `mongod` processes; if you are sharing your cluster with unrelated apps, the `oplogger` user will be able to see **all changes to all databases** in your cluster. You will need Mongo administrator credentials to create this user. 31 | 32 | Log in to the `admin` database with the Mongo shell using your administrator credentials. (You must connect to the current primary in your replica set; if the prompt says `SECONDARY` instead of `PRIMARY`, type `db.isMaster()` in the `mongo` shell and try again connecting to the server listed under `primary`.) 33 | 34 | ``` 35 | $ mongo -u YourExistingAdminUserName -p YourExistingAdminPassword mongo-server-1.example.com/admin 36 | ``` 37 | 38 | Now run the following command to make an `oplogger` user with the ability to read collections in the `local` database. 39 | 40 | If you are using Mongo 2.6 (though see the "Note for Mongo 2.6 users" below if you are running Meteor 1.0.3.2 or earlier): 41 | 42 | ``` 43 | cluster:PRIMARY> db.createUser({user: "oplogger", pwd: "PasswordForOplogger", roles: [{role: "read", db: "local"}]}) 44 | ``` 45 | 46 | If you are using Mongo 2.4: 47 | 48 | ``` 49 | cluster:PRIMARY> db.addUser({user: "oplogger", pwd: "PasswordForOplogger", roles: [], otherDBRoles: {local: ["read"]}}) 50 | ``` 51 | 52 | Then, when running your bundled Meteor app, set the `MONGO_OPLOG_URL` environment variable: 53 | 54 | ``` 55 | MONGO_OPLOG_URL=mongodb://oplogger:PasswordForOplogger@mongo-server-1.example.com,mongo-server-2.example.com,mongo-server-3.example.com/local?authSource=admin&replicaSet=replicaSetName 56 | ``` 57 | 58 | (You can find the name of your replica set by running `rs.config()._id` in the mongo console). 59 | 60 | (You may be used to running `db.createUser` (or `db.addUser`) inside the actual database that you want the new user to be able to access (in this case, `local`), instead of running it in `admin` and using the `authSource` flag to specify that you want to authenticate against `admin`. However, this doesn't work with the special case of the `local` database. Mongo 2.6 specifically prevents you from creating users in the `local` database, and while Mongo 2.4 would let you do it, you would find that you need to run `db.addUser` separately against each database replica (and risking ending up with different passwords on each), because the `local` database is not itself replicated across servers.) 61 | 62 | (Note for Mongo 2.6 users: releases of Meteor prior to 1.0.4 were not tested with Mongo 2.6, and you will need to set up a custom user-defined role that can access the `system.replset` collection in order to use these versions of Meteor with Mongo 2.6. See [this comment by @rwillmer](https://github.com/meteor/meteor/issues/2036#issuecomment-40196242) for instructions. Meteor 1.0.4 does not require special permissions on `system.replset` and the simpler `createUser` call above will work.) 63 | 64 | `OplogObserveDriver` is currently not supported for apps deployed with `meteor deploy`. Galaxy (the in-progress replacement for the `meteor deploy` servers which currently hosts some of Meteor Development Group's sites) will support `OplogObserveDriver`. 65 | 66 | 67 | ### `OplogObserveDriver` during development 68 | 69 | Starting with Meteor 0.7.0, whenever you run your local development server, the `meteor` tool will set up your local development database as a single-member replica set and automatically enable `OplogObserveDriver`. 70 | 71 | Our goal is for `OplogObserveDriver` to be indistinguishable from `PollingObserveDriver` (except that it should notice database changes faster). However, if you do find a bug in `OplogObserveDriver` that affects your app's development, then (after [reporting it](https://github.com/meteor/meteor/wiki/Contributing-to-Meteor#reporting-a-bug-in-meteor)), you can disable `OplogObserveDriver` as a workaround. 72 | 73 | There are several ways to do so. For a specific query, you can specify the undocumented option `_disableOplog` to the find call: 74 | 75 | ```js 76 | Meteor.publish("comments", function (postId) { 77 | return Comments.find({post: postId}, {_disableOplog: true, fields: {secret: 0}}); 78 | }); 79 | ``` 80 | 81 | Or to entirely disable `OplogObserveDriver` for your app, run `$ meteor add disable-oplog`. (And please let us know why!) 82 | 83 | When running Meteor's internal tests with `meteor test-packages`, pass the `--disable-oplog` flag to disable `OplogObserveDriver`. (We do this during our pre-release QA process, to ensure that our tests pass with both observe drivers.) 84 | 85 | 86 | ### How to tell if your queries are using `OplogObserveDriver` 87 | 88 | For now, we only have a crude way to tell how many `observeChanges` calls are using `OplogObserveDriver`, and not which calls they are. 89 | 90 | This uses the `facts` package, an internal Meteor package that exposes real-time metrics for the current Meteor server. In your app, run `$ meteor add facts`, and add the `{{> serverFacts}}` template to your app. If you are using the `autopublish` package, Meteor will automatically publish all metrics to all users. If you are not using `autopublish`, you will have to tell Meteor which users can see your metrics by calling `Facts.setUserIdFilter` in server code; for example: 91 | 92 | ```js 93 | Facts.setUserIdFilter(function (userId) { 94 | var user = Meteor.users.findOne(userId); 95 | return user && user.admin; 96 | }); 97 | ``` 98 | 99 | (When running your app locally, `Facts.setUserIdFilter(function () { return true; });` may be good enough!) 100 | 101 | Now look at your app. The `facts` template will render a variety of metrics; the ones we're looking for are `observe-drivers-oplog` and `observe-drivers-polling` in the `mongo-livedata` section. If `observe-drivers-polling` is zero or not rendered at all, then all of your `observeChanges` calls are using `OplogObserveDriver`! 102 | -------------------------------------------------------------------------------- /long-form/package-server-api.md: -------------------------------------------------------------------------------- 1 | Meteor Isobuild is a complete toolchain for building an app from its source code into a set of runnable programs. As part of that toolchain, we run the Meteor Packaging Server, which stores Meteor packages that have been published with `meteor publish` and keeps track of their usage. 2 | 3 | ## Package Metadata 4 | 5 | Meteor provides a DDP interface to get general information about packages and their versions that are available on the package server, at packages.meteor.com. 6 | 7 | syncNewPackageData(syncToken) 8 | 9 | This will return 500 or so records per collection from the package database. It may be useful to call this method multiple times, with syncTokens returned by the previous call, until it has returned all of the latest data. 10 | 11 | Arguments: 12 | 13 | * `syncToken`: (Object) the syncToken represents the current state of the client database. When the client database is empty, pass in the default syncToken (see below). Afterwards, pass in the syncToken returned by the previous call to syncNewData. (See below for details) 14 | 15 | Returns an object with the following keys: 16 | 17 | * `collections` : This is the package data, represented by an object with the following keys: 18 | * `packages`: up to 500 records from the `packages` collection (see: Metadata Collections) 19 | * `versions`: up to 500 records from the `versions` collection (see: Metadata Collections) 20 | * `builds`: up to 500 records from the `builds` collection (see: Metadata Collections) 21 | * `releaseTracks`: up to 500 records from the `releases` collection (see: Metadata Collections) 22 | * `releaseVersions`: up to 500 records from the `releaseVersions` collection (see: Metadata Collections) 23 | * `syncToken`: (Object) a new syncToken, representing the result of this sync. 24 | * `upToDate`: (Boolean) True, if this is the latest data. To make sure you`ve synced all of the data, call syncNewPackageData repeatedly until this is true. 25 | * `resetData`: (Boolean) Some data has been deleted, and the resync process has begone from scratch. If this is true, delete the results from all previous syncs. 26 | 27 | In addition to the sync method, the Meteor package server publishes the following dataset: 28 | 29 | `defaults`: This dataset exposes the collection `defaults` and takes no arguments. It contains exactly one record, with the following keys: 30 | 31 | * `syncToken`: the base sync token. Pass this in the first time that you run `syncNewData`. 32 | 33 | `changes`: This dataset exposes the collection `changes` and takes no arguments. It contains several records, with the following keys: 34 | 35 | * `collection`: (String) collection name, such as `packages` or `versions` (see: [Metadata Collections](https://github.com/meteor/meteor/wiki/Meteor-Package-Server-API#metadata-collections)) 36 | * `changes`: (Number) Incremented every time a document in that collection is changed or added. 37 | 38 | ## Package Use Statistics 39 | 40 | Meteor aims to provide easy user access to statistics on package installs. Specifically, every day, at slightly past UTC midnight, we run a job to aggregate package adds over the previous UTC day and store it in a publicly available file. To get it, make an HTTP GET request to: 41 | 42 | ` packages.meteor.com/stats/v1/` 43 | 44 | In the document, stats are represented as a series of JSON objects, separated by new lines, for example, as so: 45 | 46 | ` { "name": "tracker", "version": "1.0.1", "directAdds": 250, "totalAdds": 1000 }` 47 | 48 | ` { "name": "iron:router", "version": "1.0.0", "directAdds": 200, "totalAdds": 800 }` 49 | 50 | The keys are as follows: 51 | 52 | * `name`: (String) name of the package, such as `blaze`, `tracker` or `iron:router` 53 | * `version`: (String) version being added. 54 | * `directAdds`: (Number) number of times this package was *directly* added to an app in the 24 hour period, for the first time — that is, the number of apps where the app maintainer ran `meteor add ` and, as a result, Meteor added @ to their app. In the example above, 200 people ran `meteor add iron:router`, adding `iron:router@1.0.0` to 200 apps. 55 | * `totalAdds`: (Number) total number of times this package was added to an app in the 24 hour period, for the first time. Unlike the `directAdds`, this also includes the number of times that a package was added to an app as a dependency of a different package. For example, let's say someone runs `meteor add foo:bar` to add `foo:bar`, which depends on `tracker` to an app. Because foo:bar depends on `tracker`, `tracker` gets added to their app. The totalAdds number for `tracker` still gets incremented, but the `directAdds` number stays the same. 56 | 57 | The packages server publishes a dataset called `stats`, that exposes one collection called `stats` and takes no arguments. It contains one record with the keys: 58 | 59 | * `earliest`: (String) Earliest date for which stats are available, in the form `YYYY-MM-DD`. 60 | * `latest`: (String) Latest date for which stats are available, in the form `YYYY-MM-DD`. 61 | 62 | ## Metadata Collections 63 | 64 | Package information comes in three collections, which we sync to the client with the DDP method `syncNewData`. That method looks at the latest time the record was updated, which means that, over a long period of time, the same record might be sent to the client more than once (with different contents). The collections are as follows. 65 | 66 | ### Packages 67 | 68 | Stores the general metadata about each package and contains one record per package. Some of its fields are as follows (there may be other, undocumented fields): 69 | 70 | * `name`: (String) name of the package (ex: `iron:router`) 71 | * `maintainers`: (Array of objects, containing key `username`) List of authorized package maintainers 72 | * `homepage`: (String) the URL of the package homepage, as set with `meteor admin change-homepage` 73 | * `lastUpdated`: (Date) the last time this record was updated 74 | 75 | ### Versions 76 | 77 | Stores the per-version metadata about each version of each package. Contains one record for each package version. Some of the fields are as follows (there may be other, undocumented fields): 78 | 79 | * `packageName`: (String) name of the package 80 | * `version`: (String) Version number. 81 | * `description`: (String) short, one sentence description of this version, as defined in Package.describe 82 | * `longDescription`: (String) a longer description, usually excerpted from README.md 83 | * `git`: (String) this versions`s Git URL 84 | * `source`: object representing the version`s source, with the following keys: 85 | * `url`: (String) URL containing the source tarball 86 | * `hash`: (String) SHA256 hash of the source tarball 87 | * `readme`: object representing the version`s README file, with the following keys: 88 | * `url`: (String) URL the README contents 89 | * `hash`: (String) SHA256 hash of the README contents 90 | * `dependencies`: object representing the dependencies of this version. The keys are package names, and the values are objects with keys: 91 | * `constraint`: (String) version constraint for that dependency. For example, `1.0.0` or `=1.0.1`. 92 | * `references`: an object with the following keys: 93 | * `arch`: (String) dependency target, one of: `os`, `browser.web` or `browser.cordova` 94 | * `weak`: (Boolean) True if this is a weak dependency for that target 95 | * `unordered`: (Boolean) True if this is an unordered dependency for that target 96 | * `implied`: (Boolean) True if this dependency is implied for that target 97 | * `exports`: an array of objects, representing the variables this package version exports. Exports have the following keys: 98 | * `name`: (String) name of the exported variable, such as `Router` 99 | * `architectures`: Array of strings with values that are one of: `os`, `browser.web` or `browser.cordova` 100 | * `unmigrated`: (Boolean) true if this version is NOT compatible with Meteor 0.9.0 101 | * `debugOnly`: (Boolean) set to true if this is a debug-only package 102 | * `publishedBy`: (Object, containing key `username`) user that published this package 103 | * `published`: (Date) publication date of this version 104 | * `lastUpdated`: (Date) the last time this record was updated 105 | * `_id`: Mongo ID for this version record. 106 | 107 | >Note: Meteor package names can include periods. This means that the keys in the `dependencies` field may include periods, which is an invalid object schema in many databases, including Mongo. 108 | 109 | ### Builds 110 | 111 | Stores one record for each OS build of each version of each package. A package version with no binary dependencies will only have one build. For a package with binary dependencies, a build record will be created whenever its publisher successfully runs `meteor publish-for-arch`. Some of the fields are as follows (there may be other, undocumented fields): 112 | 113 | * `versionId` : The _id of the version record for this package name and version 114 | * `buildArchitectures`: (String) the OS architectures for this build separated by `+`, such as `os+web.browser+web.cordova`. 115 | * `build`: object showing where to find the Isopack for this package, version and architecture. Has the following keys: 116 | * `url`: (String) URL of the tarball 117 | * `tarballHash`: (String) SHA256 hash of the tarball 118 | * `treeHash`: (String) SHA256 hash of the contents of the tarball 119 | * `builtBy`:(Object containing key `username`) user who ran `meteor publish` or `meteor publish-for-arch` for this architecture 120 | * `buildPublished`: (Date) time of publication 121 | * `lastUpdated`: (Date) last time this record was updated 122 | 123 | ### Release Tracks 124 | 125 | Stores one record for each release track. (For example, METEOR is a release track, with versions like `1.0` and `1.0.4-rc.0.). Records have the following keys: 126 | 127 | * `name`: (String) name of the track, such as `METEOR` 128 | * `maintainers`: (Array of objects with key `username`) array representing the release maintainers 129 | * `lastUpdated`: (Date) last time this record was updated 130 | 131 | ### Release versions 132 | 133 | Stores one record for each release version. Records have the following keys: 134 | 135 | * `track`: (String) name of the track, such as `METEOR` 136 | * `version`: (String) release version, such as `1.0` or `1.0.1-albatross` 137 | * `description`: (String) short description of the release version 138 | * `recommended`: (Boolean) true for recommended releases 139 | * `tool`: (String) version of the Meteor tool to be used in this release, such as `meteor-tool@1.0.39` 140 | * `packages`: (Object, keyed from package name to version) Package versions included in this release. 141 | * `orderKey`: (String) an order key for this release, automatically generated for semver-like release names. Meteor uses this to pick the correct release for `meteor update`. 142 | * `published`: (Date) date of publication of this release version 143 | * `publishedBy`: (Object with key `username`) user who published this release 144 | * `lastUpdated`: (Date) last time this record was updated 145 | 146 | > Note: Meteor package names can include periods. This means that the keys in the `packages` field may include periods, which is an invalid object schema in many databases, including Mongo. 147 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "public" 3 | command = "npm install && npm run build" 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hexo-site", 3 | "version": "0.0.0", 4 | "private": true, 5 | "hexo": { 6 | "version": "3.9.0" 7 | }, 8 | "devDependencies": { 9 | "canonical-json": "0.0.4", 10 | "chexo": "1.0.7", 11 | "handlebars": "4.4.3", 12 | "hexo": "3.9.0", 13 | "hexo-prism-plus": "1.1.0", 14 | "hexo-renderer-ejs": "1.0.0", 15 | "hexo-renderer-less": "0.2.0", 16 | "hexo-renderer-marked": "2.0.0", 17 | "hexo-server": "1.0.0", 18 | "hexo-versioned-netlify-redirects": "1.1.0", 19 | "jsdoc": "3.5.5", 20 | "meteor-hexo-config": "1.0.10", 21 | "meteor-theme-hexo": "2.0.1", 22 | "showdown": "1.9.0", 23 | "underscore": "1.9.1" 24 | }, 25 | "scripts": { 26 | "build": "jsdoc/jsdoc.sh && chexo meteor-hexo-config -- generate", 27 | "clean": "hexo clean; rm data/data.js data/names.json", 28 | "test": "npm run clean; npm run build", 29 | "predeploy": "npm run build", 30 | "deploy": "hexo-s3-deploy", 31 | "start": "npm run build && chexo meteor-hexo-config -- server" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "meteor-docs" 4 | ], 5 | "baseBranches": [ 6 | "master", 7 | "version-1.6", 8 | "version-1.5", 9 | "version-1.4", 10 | "version-1.3" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /scripts/api-box.js: -------------------------------------------------------------------------------- 1 | /* global hexo */ 2 | 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var handlebars = require('handlebars'); 6 | var _ = require('underscore'); 7 | var parseTagOptions = require('./parseTagOptions'); 8 | var showdown = require('showdown'); 9 | var converter = new showdown.Converter(); 10 | 11 | // can't put this file in this folder annoyingly 12 | var html = fs.readFileSync(path.join(__dirname, '..', 'assets', 'api-box.html'), 'utf8'); 13 | var template = handlebars.compile(html); 14 | 15 | if (!hexo.config.api_box || !hexo.config.api_box.data_file) { 16 | throw new Error("You need to provide the location of the api box data file in config.api_box.data_file"); 17 | } 18 | 19 | var dataPath = path.join(hexo.base_dir, hexo.config.api_box.data_file); 20 | var DocsData = require(dataPath); 21 | 22 | hexo.extend.tag.register('apibox', function(args) { 23 | var name = args.shift(); 24 | var options = parseTagOptions(args) 25 | 26 | var dataFromApi = apiData({ name: name }); 27 | 28 | if (! dataFromApi) { 29 | throw new Error("Cannot render apibox without API data: " + name); 30 | return; 31 | } 32 | 33 | var defaults = { 34 | // by default, nest if it's a instance method 35 | nested: name.indexOf('#') !== -1, 36 | instanceDelimiter: '#' 37 | }; 38 | var data = _.extend({}, defaults, options, dataFromApi); 39 | 40 | data.id = data.longname.replace(/[.#]/g, "-"); 41 | 42 | data.signature = signature(data, { short: false }); 43 | data.title = signature(data, { 44 | short: true, 45 | instanceDelimiter: data.instanceDelimiter, 46 | }); 47 | data.importName = importName(data); 48 | data.paramsNoOptions = paramsNoOptions(data); 49 | 50 | return template(data); 51 | }); 52 | 53 | 54 | var apiData = function (options) { 55 | options = options || {}; 56 | if (typeof options === "string") { 57 | options = {name: options}; 58 | } 59 | 60 | var root = DocsData[options.name]; 61 | 62 | if (! root) { 63 | console.log("API Data not found: " + options.name); 64 | } 65 | 66 | if (_.has(options, 'options')) { 67 | root = _.clone(root); 68 | var includedOptions = options.options.split(';'); 69 | root.options = _.filter(root.options, function (option) { 70 | return _.contains(includedOptions, option.name); 71 | }); 72 | } 73 | 74 | return root; 75 | }; 76 | 77 | signature = function (data, options) { 78 | var escapedLongname = _.escape(data.longname); 79 | 80 | var paramsStr = ''; 81 | 82 | if (!options.short) { 83 | if (data.istemplate || data.ishelper) { 84 | var params = data.params; 85 | 86 | var paramNames = _.map(params, function (param) { 87 | var name = param.name; 88 | 89 | name = name + "=" + name; 90 | 91 | if (param.optional) { 92 | return "[" + name + "]"; 93 | } 94 | 95 | return name; 96 | }); 97 | 98 | paramsStr = ' ' + paramNames.join(" ") + ' '; 99 | } else { 100 | // if it is a function, and therefore has arguments 101 | if (_.contains(["function", "class"], data.kind)) { 102 | var params = data.params; 103 | 104 | var paramNames = _.map(params, function (param) { 105 | if (param.optional) { 106 | return "[" + param.name + "]"; 107 | } 108 | 109 | return param.name; 110 | }); 111 | 112 | paramsStr= "(" + paramNames.join(", ") + ")"; 113 | } 114 | } 115 | } 116 | 117 | if (data.istemplate) { 118 | return '{{> ' + escapedLongname + paramsStr + ' }}'; 119 | } else if (data.ishelper){ 120 | return '{{ ' + escapedLongname + paramsStr + ' }}'; 121 | } else { 122 | if (data.kind === "class" && !options.short) { 123 | escapedLongname = 'new ' + escapedLongname; 124 | } 125 | 126 | // In general, if we are looking at an instance method, we want to show it as 127 | // Something#foo or #foo (if short). However, when it's on something called 128 | // `this`, we'll do the slightly weird thing of showing `this.foo` in both cases. 129 | if (data.scope === "instance") { 130 | // the class this method belongs to. 131 | var memberOfData = apiData(data.memberof) || apiData(`${data.memberof}#${data.memberof}`); 132 | 133 | // Certain instances are provided to the user in scope with a specific name 134 | // TBH I'm not sure what else we use instanceName for (why we are setting for 135 | // e.g. `reactiveVar` if we don't want to show it here) but we opt into showing it 136 | if (memberOfData.showinstancename) { 137 | escapedLongname = memberOfData.instancename + "." + data.name; 138 | } else if (options.short) { 139 | // Something#foo => #foo 140 | return options.instanceDelimiter + escapedLongname.split('#')[1]; 141 | } 142 | } 143 | 144 | // If the user passes in a instanceDelimiter and we are a static method, 145 | // we are probably underneath a heading that defines the object (e.g. DDPRateLimiter) 146 | if (data.scope === "static" && options.instanceDelimiter && options.short) { 147 | // Something.foo => .foo 148 | return options.instanceDelimiter + escapedLongname.split('.')[1]; 149 | } 150 | 151 | return escapedLongname + paramsStr; 152 | } 153 | }; 154 | 155 | var importName = function(doc) { 156 | const noImportNeeded = !doc.module 157 | || doc.scope === 'instance' 158 | || doc.ishelper 159 | || doc.istemplate; 160 | 161 | // override the above we've explicitly decided to (i.e. Template.foo.X) 162 | if (!noImportNeeded || doc.importfrompackage) { 163 | if (doc.memberof) { 164 | return doc.memberof.split('.')[0]; 165 | } else { 166 | return doc.name; 167 | } 168 | } 169 | }; 170 | 171 | var paramsNoOptions = function (doc) { 172 | return _.reject(doc.params, function (param) { 173 | return param.name === "options"; 174 | }); 175 | }; 176 | 177 | var typeLink = function (displayName, url) { 178 | return "" + displayName + ""; 179 | }; 180 | 181 | var toOrSentence = function (array) { 182 | if (array.length === 1) { 183 | return array[0]; 184 | } else if (array.length === 2) { 185 | return array.join(" or "); 186 | } 187 | 188 | return _.initial(array).join(", ") + ", or " + _.last(array); 189 | }; 190 | 191 | var typeNameTranslation = { 192 | "function": "Function", 193 | EJSON: typeLink("EJSON-able Object", "#ejson"), 194 | EJSONable: typeLink("EJSON-able Object", "#ejson"), 195 | "Tracker.Computation": typeLink("Tracker.Computation", "#tracker_computation"), 196 | MongoSelector: [ 197 | typeLink("Mongo Selector", "#selectors"), 198 | typeLink("Object ID", "#mongo_object_id"), 199 | "String" 200 | ], 201 | MongoModifier: typeLink("Mongo Modifier", "#modifiers"), 202 | MongoSortSpecifier: typeLink("Mongo Sort Specifier", "#sortspecifiers"), 203 | MongoFieldSpecifier: typeLink("Mongo Field Specifier", "collections.html#fieldspecifiers"), 204 | JSONCompatible: "JSON-compatible Object", 205 | EventMap: typeLink("Event Map", "#eventmaps"), 206 | DOMNode: typeLink("DOM Node", "https://developer.mozilla.org/en-US/docs/Web/API/Node"), 207 | "Blaze.View": typeLink("Blaze.View", "#blaze_view"), 208 | Template: typeLink("Blaze.Template", "#blaze_template"), 209 | DOMElement: typeLink("DOM Element", "https://developer.mozilla.org/en-US/docs/Web/API/element"), 210 | MatchPattern: typeLink("Match Pattern", "#matchpatterns"), 211 | "DDP.Connection": typeLink("DDP Connection", "#ddp_connect") 212 | }; 213 | 214 | handlebars.registerHelper('typeNames', function typeNames (nameList) { 215 | // change names if necessary 216 | nameList = _.map(nameList, function (name) { 217 | // decode the "Array." syntax 218 | if (name.slice(0, 7) === "Array.<") { 219 | // get the part inside angle brackets like in Array 220 | name = name.match(/<([^>]+)>/)[1]; 221 | 222 | if (name && typeNameTranslation.hasOwnProperty(name)) { 223 | return "Array of " + typeNameTranslation[name] + "s"; 224 | } 225 | 226 | if (name) { 227 | return "Array of " + name + "s"; 228 | } 229 | 230 | console.log("no array type defined"); 231 | return "Array"; 232 | } 233 | 234 | if (typeNameTranslation.hasOwnProperty(name)) { 235 | return typeNameTranslation[name]; 236 | } 237 | 238 | if (DocsData[name]) { 239 | return typeNames(DocsData[name].type); 240 | } 241 | 242 | return name; 243 | }); 244 | 245 | nameList = _.flatten(nameList); 246 | 247 | return toOrSentence(nameList); 248 | }); 249 | 250 | handlebars.registerHelper('markdown', function(text) { 251 | return converter.makeHtml(text); 252 | }); 253 | 254 | handlebars.registerHelper('hTag', function() { 255 | return this.nested ? 'h3' : 'h2'; 256 | }); -------------------------------------------------------------------------------- /scripts/changelog.js: -------------------------------------------------------------------------------- 1 | /* global hexo */ 2 | 3 | var fs = require('fs'); 4 | var showdown = require('showdown'); 5 | var converter = new showdown.Converter({ 6 | disableForced4SpacesIndentedSublists: true, 7 | }); 8 | 9 | // Read the file given, strip of #vNEXT, render w/ markdown 10 | hexo.extend.tag.register('changelog', function(args) { 11 | var str = fs.readFileSync(args[0], 'utf8'); 12 | // Remove everything from `## v.NEXT` until the next H2 (released) heading. 13 | str = str.replace(/^## v\.NEXT.*?^(?=##[^#])/ms, ''); 14 | return converter.makeHtml(str); 15 | }); 16 | -------------------------------------------------------------------------------- /scripts/dl.js: -------------------------------------------------------------------------------- 1 | /* global hexo */ 2 | 3 | var parseTagOptions = require('./parseTagOptions'); 4 | 5 | hexo.extend.tag.register('dtdd', function(args, content) { 6 | var options = parseTagOptions(args); 7 | 8 | var typespan = ''; 9 | if (options.type) { 10 | typespan = '' + options.type + ''; 11 | } 12 | 13 | var idstr = ''; 14 | if (options.id) { 15 | idstr = 'id="' + options.id + '"'; 16 | } 17 | var namespan = '' + options.name + ''; 18 | 19 | return hexo.render.render({text: content, engine: 'md'}) 20 | .then(function(markdownContent) { 21 | return '
' + namespan + typespan + '
' + markdownContent + '
'; 22 | }); 23 | }, { ends: true, async: true }); 24 | -------------------------------------------------------------------------------- /scripts/nameToId.js: -------------------------------------------------------------------------------- 1 | // get the docs id from the name 2 | module.exports = { 3 | "Meteor.isClient": "meteor_isclient", 4 | "Meteor.isServer": "meteor_isserver", 5 | "Meteor.startup": "meteor_startup", 6 | "Meteor.users": "meteor_users", 7 | "Tracker.autorun": "tracker_autorun", 8 | "Mongo.Collection#find": "find", 9 | "Meteor.status": "meteor_status", 10 | "Meteor.user": "meteor_user", 11 | "Meteor.userId": "meteor_userid", 12 | "Meteor.loggingIn": "meteor_loggingin", 13 | "Mongo.Cursor#observe": "observe", 14 | "Mongo.Cursor#observeChanges": "observe_changes", 15 | "Subscription#added": "publish_added", 16 | "Subscription#changed": "publish_changed", 17 | "Subscription#removed": "publish_removed", 18 | "Subscription#ready": "publish_ready", 19 | "Meteor.Error": "meteor_error", 20 | "Meteor.onConnection": "meteor_onconnection", 21 | "Mongo.Collection": "mongo_collection", 22 | "Mongo.Collection#insert": "insert", 23 | "Mongo.Collection#update": "update", 24 | "Mongo.Collection#remove": "remove", 25 | "Meteor.reconnect": "meteor_reconnect", 26 | "Meteor.methods": "meteor_methods", 27 | "Meteor.disconnect": "meteor_disconnect", 28 | "DDP.connect": "ddp_connect", 29 | "Mongo.ObjectID": "mongo_object_id", 30 | "Mongo.Collection#allow": "allow", 31 | "Meteor.publish": "meteor_publish", 32 | "Mongo.Collection#deny": "deny", 33 | "Mongo.Collection#upsert": "upsert", 34 | "Mongo.Cursor#forEach": "foreach", 35 | "Mongo.Cursor#map": "map", 36 | "Mongo.Cursor#fetch": "fetch", 37 | "EJSON.equals": "ejson_equals", 38 | "Mongo.Collection#findOne": "findone", 39 | "Session.get": "session_get", 40 | "Session.set": "session_set", 41 | "Subscription#userId": "publish_userId", 42 | "DDPCommon.MethodInvocation#userId": "method_userId", 43 | "AccountsClient": "accountsclient", 44 | "AccountsServer": "accountsserver", 45 | "Accounts.verifyEmail": "accounts_verifyemail", 46 | "Accounts.forgotPassword": "accounts_forgotpassword", 47 | "Accounts.createUser": "accounts_createuser", 48 | "AccountsCommon#userId": "accounts_userid", 49 | "AccountsCommon#user": "accounts_user", 50 | "AccountsCommon#config": "accounts_config", 51 | "AccountsCommon#onLogin": "accounts_onlogin", 52 | "AccountsCommon#onLoginFailure": "accounts_onloginfailure", 53 | "AccountsClient#loggingIn": "accounts_loggingin", 54 | "AccountsServer#validateLoginAttempt": "accounts_validateloginattempt", 55 | "AccountsServer#validateNewUser": "accounts_validatenewuser", 56 | "Accounts.setPassword": "accounts_setpassword", 57 | "Accounts.sendEnrollmentEmail": "accounts_sendenrollmentemail", 58 | "AccountsServer#onCreateUser": "accounts_oncreateuser", 59 | "Accounts.sendResetPasswordEmail": "accounts_sendresetpasswordemail", 60 | "Accounts.resetPassword": "accounts_resetpassword", 61 | "Accounts.sendVerificationEmail": "accounts_sendverificationemail", 62 | "Accounts.emailTemplates": "accounts_emailtemplates", 63 | "Accounts.onResetPasswordLink": "Accounts-onResetPasswordLink", 64 | "Accounts.onEnrollmentLink": "Accounts-onEnrollmentLink", 65 | "Accounts.onEmailVerificationLink": "Accounts-onEmailVerificationLink", 66 | "Tracker.flush": "tracker_flush", 67 | "Tracker.afterFlush": "tracker_afterflush", 68 | "Tracker.active": "tracker_active", 69 | "Tracker.Dependency#depend": "dependency_depend", 70 | "Tracker.onInvalidate": "tracker_oninvalidate", 71 | "Tracker.Computation#onInvalidate": "computation_oninvalidate", 72 | "Tracker.Computation#onStop": "computation_onstop", 73 | "Tracker.Computation#stop": "computation_stop", 74 | "Tracker.currentComputation": "tracker_currentcomputation", 75 | "Session.equals": "session_equals", 76 | "EJSON.newBinary": "ejson_new_binary", 77 | "EJSON.addType": "ejson_add_type", 78 | "DDPCommon.MethodInvocation#unblock": "method_unblock", 79 | "Accounts.ui.config": "accounts_ui_config", 80 | "Meteor.settings": "meteor_settings", 81 | "undefined": "meteorpublishforarch", 82 | "Meteor.wrapAsync": "meteor_wrapasync", 83 | "Meteor.absoluteUrl": "meteor_absoluteurl", 84 | "Meteor.release": "meteor_release", 85 | "Subscription#onStop": "publish_onstop", 86 | "Subscription#error": "publish_error", 87 | "Subscription#stop": "publish_stop", 88 | "Subscription#connection": "publish_connection", 89 | "DDPCommon.MethodInvocation#setUserId": "method_setUserId", 90 | "DDPCommon.MethodInvocation#isSimulation": "method_issimulation", 91 | "DDPCommon.MethodInvocation#connection": "method_connection", 92 | "Mongo.Cursor#count": "count", 93 | "Session.setDefault": "session_set_default", 94 | "AccountsClient#logout": "accounts_logout", 95 | "AccountsClient#logoutOtherClients": "accounts_logoutotherclients", 96 | "Meteor.logout": "meteor_logout", 97 | "Meteor.logoutOtherClients": "meteor_logoutotherclients", 98 | "Meteor.loginWithPassword": "meteor_loginwithpassword", 99 | "Accounts.changePassword": "accounts_changepassword", 100 | "Meteor.setTimeout": "meteor_settimeout", 101 | "Meteor.setInterval": "meteor_setinterval", 102 | "Meteor.clearTimeout": "meteor_cleartimeout", 103 | "Meteor.clearInterval": "meteor_clearinterval", 104 | "Tracker.nonreactive": "tracker_nonreactive", 105 | "Tracker.Computation#invalidate": "computation_invalidate", 106 | "Tracker.Computation#stopped": "computation_stopped", 107 | "Tracker.Computation#invalidated": "computation_invalidated", 108 | "Tracker.Computation#firstRun": "computation_firstrun", 109 | "Tracker.Dependency#changed": "dependency_changed", 110 | "Tracker.Dependency#hasDependents": "dependency_hasdependents", 111 | "EJSON.parse": "ejson_parse", 112 | "EJSON.stringify": "ejson_stringify", 113 | "EJSON.fromJSONValue": "ejson_from_json_value", 114 | "EJSON.toJSONValue": "ejson_to_json_value", 115 | "EJSON.clone": "ejson_clone", 116 | "EJSON.isBinary": "ejson_is_binary", 117 | "HTTP.call": "http_call", 118 | "Meteor.isCordova": "meteor_iscordova", 119 | "Template#events": "template_events", 120 | "Template#helpers": "template_helpers", 121 | "Template#rendered": "template_rendered", 122 | "Template#created": "template_created", 123 | "Template#destroyed": "template_destroyed", 124 | "Template#onRendered": "template_onRendered", 125 | "Template#onCreated": "template_onCreated", 126 | "Template#onDestroyed": "template_onDestroyed", 127 | "Blaze.TemplateInstance#findAll": "template_findAll", 128 | "Blaze.TemplateInstance#$": "template_$", 129 | "Blaze.TemplateInstance#find": "template_find", 130 | "Blaze.TemplateInstance#firstNode": "template_firstNode", 131 | "Blaze.TemplateInstance#lastNode": "template_lastNode", 132 | "Blaze.TemplateInstance#data": "template_data", 133 | "Blaze.TemplateInstance#autorun": "template_autorun", 134 | "Blaze.TemplateInstance#view": "template_view", 135 | "Template.registerHelper": "template_registerhelper", 136 | "Template.instance": "template_instance", 137 | "Template.currentData": "template_currentdata", 138 | "Template.parentData": "template_parentdata", 139 | "Template.body": "template_body", 140 | "ReactiveVar": "reactivevar", 141 | "ReactiveVar#get": "reactivevar_get", 142 | "ReactiveVar#set": "reactivevar_set", 143 | "HTTP.get": "http_get", 144 | "HTTP.post": "http_post", 145 | "HTTP.put": "http_put", 146 | "HTTP.del": "http_del", 147 | "Email.send": "email_send", 148 | "Assets.getText": "assets_getText", 149 | "Assets.getBinary": "assets_getBinary", 150 | "Blaze.render": "blaze_render", 151 | "Blaze.renderWithData": "blaze_renderwithdata", 152 | "Blaze.View": "blaze_view", 153 | "Blaze.remove": "blaze_remove", 154 | "Blaze.getData": "blaze_getdata", 155 | "Blaze.toHTML": "blaze_tohtml", 156 | "Blaze.toHTMLWithData": "blaze_tohtmlwithdata", 157 | "Blaze.currentView": "blaze_currentview", 158 | "Blaze.getView": "blaze_getview", 159 | "Blaze.With": "blaze_with", 160 | "Blaze.If": "blaze_if", 161 | "Blaze.Unless": "blaze_unless", 162 | "Blaze.Each": "blaze_each", 163 | "Blaze.Template": "blaze_template", 164 | "Blaze.isTemplate": "blaze_istemplate", 165 | "Meteor.subscribe": "meteor_subscribe", 166 | "Meteor.call": "meteor_call", 167 | "Meteor.apply": "meteor_apply", 168 | "PackageAPI#versionsFrom": "pack_versions", 169 | "PackageAPI#imply": "pack_api_imply", 170 | "PackageAPI#export": "pack_export", 171 | "PackageAPI#addFiles": "pack_addFiles", 172 | "PackageAPI#use": "pack_use", 173 | "Package.onUse": "pack_onUse", 174 | "Meteor.loginWith": "meteor_loginwithexternalservice", 175 | "EJSON.CustomType#typeName": "ejson_type_typeName", 176 | "EJSON.CustomType#toJSONValue": "ejson_type_toJSONValue", 177 | "EJSON.CustomType#clone": "ejson_type_clone", 178 | "EJSON.CustomType#equals": "ejson_type_equals", 179 | "Match.test": "match_test", 180 | "Template.dynamic": "template_dynamic", 181 | "currentUser": "template_currentuser", 182 | "loggingIn": "template_loggingin" 183 | }; 184 | -------------------------------------------------------------------------------- /scripts/old-redirects.js: -------------------------------------------------------------------------------- 1 | // These are the redirects that were previously setup for the docs 2 | var oldRedirects = function() { 3 | // make links backwards compatible - for example, #deps -> #tracker 4 | 5 | // Links from the old basic API into the closest full-api section 6 | var BASIC_TO_FULL_LINKS = { 7 | 'learning-resources': 'guide', 8 | 'command-line': 'commandline', 9 | 'templates': 'templates_api', 10 | 'defining-templates': 'templates_api', 11 | 'Template-helpers': 'template_helpers', 12 | 'Template-events': 'template_events', 13 | 'Template-onRendered': 'template_onRendered', 14 | 'Blaze-TemplateInstance-findAll': 'template_findAll', 15 | 'Blaze-TemplateInstance-find': 'template_find', 16 | 'session': 'session', 17 | 'Session-set': 'session_set', 18 | 'Session-get': 'session_get', 19 | 'tracker': 'tracker', 20 | 'Tracker-autorun': 'tracker_autorun', 21 | 'collections': 'collections', 22 | 'Mongo-Collection': 'mongo_collection', 23 | 'Mongo-Collection-findOne': 'findone', 24 | 'Mongo-Collection-find': 'find', 25 | 'Mongo-Collection-insert': 'insert', 26 | 'Mongo-Collection-update': 'update', 27 | 'Mongo-Collection-remove': 'remove', 28 | 'Mongo-Collection-allow': 'allow', 29 | 'Mongo-Collection-deny': 'deny', 30 | 'accounts': 'accounts_api', 31 | 'loginButtons': 'accountsui', 32 | 'Meteor-user': 'meteor_user', 33 | 'Meteor-userId': 'meteor_userid', 34 | 'Meteor-users': 'meteor_users', 35 | 'currentUser': 'template_currentuser', 36 | 'methods': 'methods_header', 37 | 'Meteor-methods': 'meteor_methods', 38 | 'Meteor-call': 'meteor_call', 39 | 'Meteor-Error': 'meteor_error', 40 | 'pubsub': 'publishandsubscribe', 41 | 'Meteor-publish': 'meteor_publish', 42 | 'Meteor-subscribe': 'meteor_subscribe', 43 | 'environment': 'core', 44 | 'Meteor-isClient': 'meteor_isclient', 45 | 'Meteor-isServer': 'meteor_isserver', 46 | 'Meteor-startup': 'meteor_startup', 47 | 'packages': 'packages', 48 | 'searchingforpackages': 'packages', 49 | 'accountsui': 'accountsui', 50 | 'coffeescript': 'coffeescript', 51 | 'email': 'email', 52 | 'http': 'http', 53 | 'less': 'less', 54 | 'markdown': 'markdown', 55 | 'underscore': 'underscore', 56 | 'spiderable': 'spiderable', 57 | }; 58 | 59 | var BASIC_TO_GUIDE_LINKS = { 60 | filestructure: 'structure.html', 61 | buildingmobileapps: 'mobile.html', 62 | quickstart: '#quickstart', 63 | }; 64 | 65 | var FULL_TO_GUIDE_LINKS = { 66 | whatismeteor: '#what-is-meteor', 67 | sevenprinciples: '#what-is-meteor', 68 | quickstart: '#quickstart', 69 | structuringyourapp: 'structure.html', 70 | dataandsecurity: 'security.html', 71 | livehtmltemplates: '#what-is-meteor', 72 | usingpackages: 'user-packages.html', 73 | namespacing: 'structure.html', 74 | deploying: 'deployment.html', 75 | writingpackages: 'writing-packages.html', 76 | }; 77 | 78 | 79 | var getRedirect = function (hash) { 80 | if (hash.indexOf("deps") !== -1) { 81 | return hash.replace("deps", "tracker"); 82 | } 83 | 84 | if (hash.indexOf("_created") !== -1) { 85 | return hash.replace("_created", "_onCreated"); 86 | } 87 | 88 | if (hash.indexOf("_rendered") !== -1) { 89 | return hash.replace("_rendered", "_onRendered"); 90 | } 91 | 92 | if (hash.indexOf("_destroyed") !== -1) { 93 | return hash.replace("_destroyed", "_onDestroyed"); 94 | } 95 | 96 | if (hash === "meteor_collection") { 97 | return "mongo_collection"; 98 | } 99 | 100 | if (hash === "collection_object_id") { 101 | return "mongo_object_id"; 102 | } 103 | 104 | if (hash === "match") { 105 | return "check_package"; 106 | } 107 | 108 | if (hash === "meteorbundle") { 109 | return "meteorbuild"; 110 | } 111 | 112 | if (hash.indexOf("reactivity") !== -1) { 113 | return "/full/tracker"; 114 | } 115 | 116 | var parts = hash.split('/'); 117 | if (parts[1] === 'basic') { 118 | var fullLink = BASIC_TO_FULL_LINKS[parts[2]]; 119 | if (fullLink) { 120 | return '/full/' + fullLink; 121 | } 122 | 123 | var guideLink = BASIC_TO_GUIDE_LINKS[parts[2]]; 124 | if (guideLink) { 125 | window.location.replace('http://guide.meteor.com/' + guideLink); 126 | } 127 | } 128 | if (parts[1] === 'full') { 129 | var guideLink = FULL_TO_GUIDE_LINKS[parts[2]]; 130 | if (guideLink) { 131 | window.location.replace('http://guide.meteor.com/' + guideLink); 132 | } 133 | } 134 | 135 | // don't redirect 136 | return false; 137 | }; 138 | 139 | var curLink = window.location.hash.slice(1); 140 | var redirect = getRedirect(curLink); 141 | 142 | if (redirect) { 143 | window.location = "#" + redirect; 144 | } 145 | } 146 | 147 | hexo.extend.tag.register('oldRedirects', function(args) { 148 | return ''; 152 | }); 153 | -------------------------------------------------------------------------------- /scripts/parseTagOptions.js: -------------------------------------------------------------------------------- 1 | // sort of hacky but allows x:y 2 | module.exports = function(args) { 3 | if (args.length === 0) { 4 | return {}; 5 | } 6 | var argsJson = '{"' + args.join('","').replace(/:/g, '":"') + '"}'; 7 | try { 8 | return JSON.parse(argsJson); 9 | } catch (e) { 10 | console.error(args, argsJson); 11 | throw new Error("Couldn't parse arguments"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /source/api/accounts-multi.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Accounts (multi-server) 3 | description: Documentation of how to use the Accounts client to connect to other servers. 4 | --- 5 | 6 | The `accounts-base` package exports two constructors, called 7 | `AccountsClient` and `AccountsServer`, which are used to create the 8 | `Accounts` object that is available on the client and the server, 9 | respectively. 10 | 11 | This predefined `Accounts` object (along with similar convenience methods 12 | of `Meteor`, such as [`Meteor.logout`](#meteor_logout)) is sufficient to 13 | implement most accounts-related logic in Meteor apps. Nevertheless, these 14 | two constructors can be instantiated more than once, to create multiple 15 | independent connections between different accounts servers and their 16 | clients, in more complicated authentication situations. 17 | 18 | {% apibox "AccountsCommon" %} 19 | 20 | The `AccountsClient` and `AccountsServer` classes share a common 21 | superclass, `AccountsCommon`. Methods defined on 22 | `AccountsCommon.prototype` will be available on both the client and the 23 | server, via the predefined `Accounts` object (most common) or any custom 24 | `accountsClientOrServer` object created using the `AccountsClient` or 25 | `AccountsServer` constructors (less common). 26 | 27 | Here are a few of those methods: 28 | 29 | {% apibox "AccountsCommon#userId" %} 30 | 31 | {% apibox "AccountsCommon#user" %} 32 | 33 | {% apibox "AccountsCommon#config" %} 34 | 35 | {% apibox "AccountsCommon#onLogin" %} 36 | 37 | See description of [AccountsCommon#onLoginFailure](#accounts_onloginfailure) 38 | for details. 39 | 40 | {% apibox "AccountsCommon#onLoginFailure" %} 41 | 42 | Either the `onLogin` or the `onLoginFailure` callbacks will be called 43 | for each login attempt. The `onLogin` callbacks are called after the 44 | user has been successfully logged in. The `onLoginFailure` callbacks are 45 | called after a login attempt is denied. 46 | 47 | These functions return an object with a single method, `stop`. Calling 48 | `stop()` unregisters the callback. 49 | 50 | On the server, the callbacks get a single argument, the same attempt info 51 | object as [`validateLoginAttempt`](#accounts_validateloginattempt). On the 52 | client, the callback argument is an object containing a single `error` 53 | property set to the `Error`-object which was received from the failed login 54 | attempt. 55 | 56 | {% apibox "AccountsCommon#onLogout" %} 57 | 58 | On the server, the `func` callback receives a single argument with the object below. On the 59 | client, no arguments are passed. 60 | 61 |
62 | {% dtdd name:"user" type:"Object" %} 63 | The Meteor user object of the user which just logged out. 64 | {% enddtdd %} 65 | 66 | {% dtdd name:"connection" type:"Object" %} 67 | The `connection` object the request came in on. See 68 | [`Meteor.onConnection`](#meteor_onconnection) for details. 69 | {% enddtdd %} 70 |
71 | 72 | {% apibox "AccountsClient" %} 73 | 74 | At most one of `options.connection` and `options.ddpUrl` should be 75 | provided in any instantiation of `AccountsClient`. If neither is provided, 76 | `Meteor.connection` will be used as the `.connection` property of the 77 | `AccountsClient` instance. 78 | 79 | Note that `AccountsClient` is currently available only on the client, due 80 | to its use of browser APIs such as `window.localStorage`. In principle, 81 | though, it might make sense to establish a client connection from one 82 | server to another remote accounts server. Please [let us 83 | know](https://github.com/meteor/meteor/wiki/Contributing-to-Meteor#feature-requests) 84 | if you find yourself needing this server-to-server functionality. 85 | 86 | 87 | These methods are defined on `AccountsClient.prototype`, and are thus 88 | available only on the client: 89 | 90 | {% apibox "AccountsClient#loggingIn" %} 91 | 92 | {% apibox "AccountsClient#logout" %} 93 | 94 | {% apibox "AccountsClient#logoutOtherClients" %} 95 | 96 | {% apibox "AccountsServer" %} 97 | 98 | These methods are defined on `AccountsServer.prototype`, and are thus 99 | available only on the server: 100 | 101 | {% apibox "AccountsServer#validateNewUser" %} 102 | 103 | This can be called multiple times. If any of the functions return `false` or 104 | throw an error, the new user creation is aborted. To set a specific error 105 | message (which will be displayed by [`accounts-ui`](#accountsui)), throw a new 106 | [`Meteor.Error`](#meteor_error). 107 | 108 | Example: 109 | 110 | ```js 111 | // Validate username, sending a specific error message on failure. 112 | Accounts.validateNewUser((user) => { 113 | if (user.username && user.username.length >= 3) { 114 | return true; 115 | } else { 116 | throw new Meteor.Error(403, 'Username must have at least 3 characters'); 117 | } 118 | }); 119 | 120 | // Validate username, without a specific error message. 121 | Accounts.validateNewUser((user) => { 122 | return user.username !== 'root'; 123 | }); 124 | ``` 125 | 126 | If the user is being created as part of a login attempt from a client (eg, 127 | calling [`Accounts.createUser`](#accounts_createuser) from the client, or 128 | [logging in for the first time with an external 129 | service](#meteor_loginwithexternalservice)), these callbacks are called *before* 130 | the [`Accounts.validateLoginAttempt`](#accounts_validateloginattempt) 131 | callbacks. If these callbacks succeed but those fail, the user will still be 132 | created but the connection will not be logged in as that user. 133 | 134 | {% apibox "AccountsServer#onCreateUser" %} 135 | 136 | Use this when you need to do more than simply accept or reject new user 137 | creation. With this function you can programatically control the 138 | contents of new user documents. 139 | 140 | The function you pass will be called with two arguments: `options` and 141 | `user`. The `options` argument comes 142 | from [`Accounts.createUser`](#accounts_createuser) for 143 | password-based users or from an external service login flow. `options` may come 144 | from an untrusted client so make sure to validate any values you read from 145 | it. The `user` argument is created on the server and contains a 146 | proposed user object with all the automatically generated fields 147 | required for the user to log in, including the `_id`. 148 | 149 | The function should return the user document (either the one passed in or a 150 | newly-created object) with whatever modifications are desired. The returned 151 | document is inserted directly into the [`Meteor.users`](#meteor_users) collection. 152 | 153 | The default create user function simply copies `options.profile` into 154 | the new user document. Calling `onCreateUser` overrides the default 155 | hook. This can only be called once. 156 | 157 | Example: 158 | 159 | ```js 160 | // Support for playing D&D: Roll 3d6 for dexterity. 161 | Accounts.onCreateUser((options, user) => { 162 | const customizedUser = Object.assign({ 163 | dexterity: _.random(1, 6) + _.random(1, 6) + _.random(1, 6), 164 | }, user); 165 | 166 | // We still want the default hook's 'profile' behavior. 167 | if (options.profile) { 168 | customizedUser.profile = options.profile; 169 | } 170 | 171 | return customizedUser; 172 | }); 173 | ``` 174 | 175 | {% apibox "AccountsServer#validateLoginAttempt" %} 176 | 177 | Call `validateLoginAttempt` with a callback to be called on login 178 | attempts. It returns an object with a single method, `stop`. Calling 179 | `stop()` unregisters the callback. 180 | 181 | When a login attempt is made, the registered validate login callbacks 182 | are called with a single argument, the attempt info object: 183 | 184 |
185 | {% dtdd name:"type" type:"String" %} 186 | The service name, such as "password" or "twitter". 187 | {% enddtdd %} 188 | 189 | {% dtdd name:"allowed" type:"Boolean" %} 190 | Whether this login is allowed and will be successful (if not aborted 191 | by any of the validateLoginAttempt callbacks). False if the login 192 | will not succeed (for example, an invalid password or the login was 193 | aborted by a previous validateLoginAttempt callback). 194 | {% enddtdd %} 195 | 196 | {% dtdd name:"error" type:"Exception" %} 197 | When `allowed` is false, the exception describing why the login 198 | failed. It will be a `Meteor.Error` for failures reported to the 199 | user (such as invalid password), and can be a another kind of 200 | exception for internal errors. 201 | {% enddtdd %} 202 | 203 | {% dtdd name:"user" type:"Object" %} 204 | When it is known which user was attempting to login, the Meteor user object. 205 | This will always be present for successful logins. 206 | {% enddtdd %} 207 | 208 | {% dtdd name:"connection" type:"Object" %} 209 | The `connection` object the request came in on. See 210 | [`Meteor.onConnection`](#meteor_onconnection) for details. 211 | {% enddtdd %} 212 | 213 | {% dtdd name:"methodName" type:"String" %} 214 | The name of the Meteor method being used to login. 215 | {% enddtdd %} 216 | 217 | {% dtdd name:"methodArguments" type:"Array" %} 218 | An array of the arguments passed to the login method. 219 | {% enddtdd %} 220 |
221 | 222 | A validate login callback must return a truthy value for the login to 223 | proceed. If the callback returns a falsy value or throws an 224 | exception, the login is aborted. Throwing a `Meteor.Error` will 225 | report the error reason to the user. 226 | 227 | All registered validate login callbacks are called, even if one of the callbacks 228 | aborts the login. The later callbacks will see the `allowed` field set to 229 | `false` since the login will now not be successful. This allows later callbacks 230 | to override an error from a previous callback; for example, you could override 231 | the "Incorrect password" error with a different message. 232 | 233 | Validate login callbacks that aren't explicitly trying to override a previous 234 | error generally have no need to run if the attempt has already been determined 235 | to fail, and should start with 236 | 237 | ```js 238 | if (!attempt.allowed) { 239 | return false; 240 | } 241 | ``` 242 | 243 | {% apibox "AccountsServer#beforeExternalLogin" %} 244 | 245 | Use this hook if you need to validate that user from an external service should 246 | be allowed to login or create account. 247 | 248 |
249 | {% dtdd name:"type" type:"String" %} 250 | The service name, such as "google" or "twitter". 251 | {% enddtdd %} 252 | 253 | {% dtdd name:"data" type:"Object" %} 254 | Data retrieved from the service 255 | {% enddtdd %} 256 | 257 | {% dtdd name:"user" type:"Object" %} 258 | If user was found in the database that matches the criteria from the service, 259 | their data will be provided here. 260 | {% enddtdd %} 261 |
262 | 263 | You should return a `Boolean` value, `true` if the login/registration should 264 | proceed or `false` if it should terminate. In case of termination 265 | the login attempt will throw an error `403`, with the message: `Login forbidden`. 266 | 267 | 268 |

Rate Limiting

269 | 270 | By default, there are rules added to the [`DDPRateLimiter`](#ddpratelimiter) 271 | that rate limit logins, new user registration and password reset calls to a 272 | limit of 5 requests per 10 seconds per session. These are a basic solution 273 | to dictionary attacks where a malicious user attempts to guess the passwords 274 | of legitimate users by attempting all possible passwords. 275 | 276 | These rate limiting rules can be removed by calling 277 | `Accounts.removeDefaultRateLimit()`. Please see the 278 | [`DDPRateLimiter`](#ddpratelimiter) docs for more information. 279 | -------------------------------------------------------------------------------- /source/api/assets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Assets 3 | description: Documentation of how to use assets in Meteor. 4 | --- 5 | 6 | > Currently, it is not possible to import `Assets` as an ES6 module. Any of the `Assets` methods below can simply be called directly in any Meteor server code. 7 | 8 | `Assets` allows server code in a Meteor application to access static server 9 | assets, which are located in the `private` subdirectory of an application's 10 | tree. Assets are not processed as source files and are copied directly 11 | into your application's bundle. 12 | 13 | {% apibox "Assets.getText" %} 14 | {% apibox "Assets.getBinary" %} 15 | {% apibox "Assets.absoluteFilePath" %} 16 | 17 | Static server assets are included by placing them in the application's `private` 18 | subdirectory. For example, if an application's `private` subdirectory includes a 19 | directory called `nested` with a file called `data.txt` inside it, then server 20 | code can read `data.txt` by running: 21 | 22 | ```js 23 | const data = Assets.getText('nested/data.txt'); 24 | ``` 25 | 26 | Note: Packages can only access their own assets. If you need to read the assets of a different package, or of the enclosing app, you need to get a reference to that package's `Assets` object. 27 | -------------------------------------------------------------------------------- /source/api/blaze.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Blaze 3 | description: Documentation of how to use Blaze, Meteor's reactive rendering engine. 4 | --- 5 | 6 | This documentation has moved to the [Blaze Community Site](http://blazejs.org). 7 | -------------------------------------------------------------------------------- /source/api/check.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Check 3 | desription: Documentation on how to use check, Meteor's type checking library. 4 | --- 5 | 6 | The `check` package includes pattern checking functions useful for checking the types and structure 7 | of variables and an [extensible library of patterns](#matchpatterns) to specify which types you are 8 | expecting. 9 | 10 | To add `check` (or `Match`) to your application, run this command in your terminal: 11 | 12 | ```bash 13 | meteor add check 14 | ``` 15 | 16 | {% apibox "check" %} 17 | 18 | Meteor methods and publish functions can take arbitrary [EJSON](#ejson) types as arguments, but most 19 | functions expect their arguments to be of a particular type. `check` is a lightweight function for 20 | checking that arguments and other values are of the expected type. For example: 21 | 22 | ```js 23 | Meteor.publish('chatsInRoom', function (roomId) { 24 | // Make sure `roomId` is a string, not an arbitrary Mongo selector object. 25 | check(roomId, String); 26 | return Chats.find({ room: roomId }); 27 | }); 28 | 29 | Meteor.methods({ 30 | addChat(roomId, message) { 31 | check(roomId, String); 32 | check(message, { 33 | text: String, 34 | timestamp: Date, 35 | // Optional, but if present must be an array of strings. 36 | tags: Match.Maybe([String]) 37 | }); 38 | 39 | // Do something with the message... 40 | } 41 | }); 42 | ``` 43 | 44 | If the match fails, `check` throws a `Match.Error` describing how it failed. If 45 | this error gets sent over the wire to the client, it will appear only as 46 | `Meteor.Error(400, 'Match Failed')`. The failure details will be written to the 47 | server logs but not revealed to the client. 48 | 49 | {% apibox "Match.test" %} 50 | 51 | `Match.test` can be used to identify if a variable has a certain structure. 52 | 53 | ```js 54 | // Will return true for `{ foo: 1, bar: 'hello' }` or similar. 55 | Match.test(value, { foo: Match.Integer, bar: String }); 56 | 57 | // Will return true if `value` is a string. 58 | Match.test(value, String); 59 | 60 | // Will return true if `value` is a string or an array of numbers. 61 | Match.test(value, Match.OneOf(String, [Number])); 62 | ``` 63 | 64 | This can be useful if you have a function that accepts several different kinds 65 | of objects, and you want to determine which was passed in. 66 | 67 |

Match Patterns

68 | 69 | The following patterns can be used as pattern arguments to 70 | [`check`](#check) and `Match.test`: 71 | 72 |
73 | {% dtdd name:"Match.Any" %} 74 | Matches any value. 75 | {% enddtdd %} 76 | 77 | {% dtdd name:"String, Number, Boolean, undefined, null" %} 78 | Matches a primitive of the given type. 79 | {% enddtdd %} 80 | 81 | {% dtdd name:"Match.Integer" %} 82 | Matches a signed 32-bit integer. Doesn't match `Infinity`, `-Infinity`, or `NaN`. 83 | {% enddtdd %} 84 | 85 | {% dtdd name:"[pattern]" %} 86 | A one-element array matches an array of elements, each of which match 87 | *pattern*. For example, `[Number]` matches a (possibly empty) array of numbers; 88 | `[Match.Any]` matches any array. 89 | {% enddtdd %} 90 | 91 |
{ key1: pattern1, key2: pattern2, ... }
92 |
93 | Matches an Object with the given keys, with values matching the given patterns. 94 | If any *pattern* is a `Match.Maybe` or `Match.Optional`, that key does not need to exist 95 | in the object. The value may not contain any keys not listed in the pattern. 96 | The value must be a plain Object with no special prototype. 97 |
98 | 99 |
Match.ObjectIncluding({ key1: pattern1, key2: pattern2, ... })
100 |
101 | Matches an Object with the given keys; the value may also have other keys 102 | with arbitrary values. 103 |
104 | 105 | {% dtdd name:"Object" %} 106 | Matches any plain Object with any keys; equivalent to 107 | `Match.ObjectIncluding({})`. 108 | {% enddtdd %} 109 | 110 | 111 | 112 | {% dtdd name:"Match.Maybe(pattern)" %} 113 | 114 | Matches either `undefined`, `null`, or _pattern_. If used in an object, matches only if the key is 115 | not set as opposed to the value being set to `undefined` or `null`. This set of conditions was 116 | chosen because `undefined` arguments to Meteor Methods are converted to `null` when sent over the 117 | wire. 118 | 119 | {% codeblock lang:js %} 120 | // In an object 121 | const pattern = { name: Match.Maybe(String) }; 122 | 123 | check({ name: 'something' }, pattern); // OK 124 | check({}, pattern); // OK 125 | check({ name: undefined }, pattern); // Throws an exception 126 | check({ name: null }, pattern); // Throws an exception 127 | 128 | // Outside an object 129 | check(null, Match.Maybe(String)); // OK 130 | check(undefined, Match.Maybe(String)); // OK 131 | {% endcodeblock %} 132 | {% enddtdd %} 133 | 134 | {% dtdd name:"Match.Optional(pattern)" %} 135 | 136 | Behaves like `Match.Maybe` except it doesn't accept `null`. If used in an object, the behavior is 137 | identical to `Match.Maybe`. 138 | 139 | {% enddtdd %} 140 | 141 | {% dtdd name:"Match.OneOf(pattern1, pattern2, ...)" %} 142 | Matches any value that matches at least one of the provided patterns. 143 | {% enddtdd %} 144 | 145 | {% dtdd name:"Any constructor function (eg, Date)" %} 146 | Matches any element that is an instance of that type. 147 | {% enddtdd %} 148 | 149 | {% dtdd name:"Match.Where(condition)" %} 150 | Calls the function *condition* with the value as the argument. If *condition* 151 | returns true, this matches. If *condition* throws a `Match.Error` or returns 152 | false, this fails. If *condition* throws any other error, that error is thrown 153 | from the call to `check` or `Match.test`. Examples: 154 | 155 | {% codeblock lang:js %} 156 | check(buffer, Match.Where(EJSON.isBinary)); 157 | 158 | const NonEmptyString = Match.Where((x) => { 159 | check(x, String); 160 | return x.length > 0; 161 | }); 162 | 163 | check(arg, NonEmptyString); 164 | {% endcodeblock %} 165 | {% enddtdd %} 166 |
167 | -------------------------------------------------------------------------------- /source/api/connections.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Server Connections 3 | description: Documentation on how to use Meteor's client-server connection. 4 | --- 5 | 6 | These functions manage and inspect the network connection between the 7 | Meteor client and server. 8 | 9 | {% apibox "Meteor.status" %} 10 | 11 | This method returns the status of the connection between the client and 12 | the server. The return value is an object with the following fields: 13 | 14 |
15 | {% dtdd name:"connected" type:"Boolean" %} 16 | True if currently connected to the server. If false, changes and 17 | method invocations will be queued up until the connection is 18 | reestablished. 19 | {% enddtdd %} 20 | 21 | {% dtdd name:"status" type:"String" %} 22 | Describes the current reconnection status. The possible 23 | values are `connected` (the connection is up and 24 | running), `connecting` (disconnected and trying to open a 25 | new connection), `failed` (permanently failed to connect; e.g., the client 26 | and server support different versions of DDP), `waiting` (failed 27 | to connect and waiting to try to reconnect) and `offline` (user has disconnected the connection). 28 | {% enddtdd %} 29 | 30 | {% dtdd name:"retryCount" type:"Number" %} 31 | The number of times the client has tried to reconnect since the 32 | connection was lost. 0 when connected. 33 | {% enddtdd %} 34 | 35 | {% dtdd name:"retryTime" type:"Number or undefined" %} 36 | The estimated time of the next reconnection attempt. To turn this 37 | into an interval until the next reconnection, use 38 | `retryTime - (new Date()).getTime()`. This key will 39 | be set only when `status` is `waiting`. 40 | {% enddtdd %} 41 | 42 | {% dtdd name:"reason" type:"String or undefined" %} 43 | If `status` is `failed`, a description of why the connection failed. 44 | {% enddtdd %} 45 |
46 | 47 | Instead of using callbacks to notify you on changes, this is 48 | a [reactive](#reactivity) data source. You can use it in a 49 | [template](#livehtmltemplates) or [computation](#tracker_autorun) 50 | to get realtime updates. 51 | 52 | {% apibox "Meteor.reconnect" %} 53 | 54 | {% apibox "Meteor.disconnect" %} 55 | 56 | Call this method to disconnect from the server and stop all 57 | live data updates. While the client is disconnected it will not receive 58 | updates to collections, method calls will be queued until the 59 | connection is reestablished, and hot code push will be disabled. 60 | 61 | Call [Meteor.reconnect](#meteor_reconnect) to reestablish the connection 62 | and resume data transfer. 63 | 64 | This can be used to save battery on mobile devices when real time 65 | updates are not required. 66 | 67 | 68 | {% apibox "Meteor.onConnection" %} 69 | 70 | `onConnection` returns an object with a single method `stop`. Calling 71 | `stop` unregisters the callback, so that this callback will no longer 72 | be called on new connections. 73 | 74 | The callback is called with a single argument, the server-side 75 | `connection` representing the connection from the client. This object 76 | contains the following fields: 77 | 78 |
79 | {% dtdd name:"id" type:"String" %} 80 | A globally unique id for this connection. 81 | {% enddtdd %} 82 | 83 | {% dtdd name:"close" type:"Function" %} 84 | Close this DDP connection. The client is free to reconnect, but will 85 | receive a different connection with a new `id` if it does. 86 | {% enddtdd %} 87 | 88 | {% dtdd name:"onClose" type:"Function" %} 89 | Register a callback to be called when the connection is closed. If the 90 | connection is already closed, the callback will be called immediately. 91 | {% enddtdd %} 92 | 93 | {% dtdd name:"clientAddress" type:"String" %} 94 | The IP address of the client in dotted form (such as `127.0.0.1`). 95 | 96 | If you're running your Meteor server behind a proxy (so that clients 97 | are connecting to the proxy instead of to your server directly), 98 | you'll need to set the `HTTP_FORWARDED_COUNT` environment variable 99 | for the correct IP address to be reported by `clientAddress`. 100 | 101 | Set `HTTP_FORWARDED_COUNT` to an integer representing the number of 102 | proxies in front of your server. For example, you'd set it to `1` 103 | when your server was behind one proxy. 104 | {% enddtdd %} 105 | 106 | {% dtdd name:"httpHeaders" type:"Object" %} 107 | When the connection came in over an HTTP transport (such as with 108 | Meteor's default SockJS implementation), this field contains 109 | whitelisted HTTP headers. 110 | 111 | Cookies are deliberately excluded from the headers as they are a 112 | security risk for this transport. For details and alternatives, see 113 | the [SockJS 114 | documentation](https://github.com/sockjs/sockjs-node#authorisation). 115 | {% enddtdd %} 116 |
117 | 118 | 119 | 120 | > Currently when a client reconnects to the server (such as after 121 | temporarily losing its Internet connection), it will get a new 122 | connection each time. The `onConnection` callbacks will be called 123 | again, and the new connection will have a new connection `id`. 124 | 125 | > In the future, when client reconnection is fully implemented, 126 | reconnecting from the client will reconnect to the same connection on 127 | the server: the `onConnection` callback won't be called for that 128 | connection again, and the connection will still have the same 129 | connection `id`. 130 | 131 | 132 | {% apibox "DDP.connect" %} 133 | 134 | To call methods on another Meteor application or subscribe to its data 135 | sets, call `DDP.connect` with the URL of the application. 136 | `DDP.connect` returns an object which provides: 137 | 138 | * `subscribe` - 139 | Subscribe to a record set. See 140 | [Meteor.subscribe](#meteor_subscribe). 141 | * `call` - 142 | Invoke a method. See [Meteor.call](#meteor_call). 143 | * `apply` - 144 | Invoke a method with an argument array. See 145 | [Meteor.apply](#meteor_apply). 146 | * `methods` - 147 | Define client-only stubs for methods defined on the remote server. See 148 | [Meteor.methods](#meteor_methods). 149 | * `status` - 150 | Get the current connection status. See 151 | [Meteor.status](#meteor_status). 152 | * `reconnect` - 153 | See [Meteor.reconnect](#meteor_reconnect). 154 | * `disconnect` - 155 | See [Meteor.disconnect](#meteor_disconnect). 156 | 157 | By default, clients open a connection to the server from which they're loaded. 158 | When you call `Meteor.subscribe`, `Meteor.status`, `Meteor.call`, and 159 | `Meteor.apply`, you are using a connection back to that default 160 | server. 161 | 162 | {% apibox "DDP.onReconnect" %} 163 | -------------------------------------------------------------------------------- /source/api/core.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Core 3 | description: Documentation of core Meteor functions. 4 | --- 5 | 6 | {% apibox "Meteor.isClient" %} 7 | {% apibox "Meteor.isServer" %} 8 | 9 | > `Meteor.isServer` can be used to limit where code runs, but it does not 10 | prevent code from being sent to the client. Any sensitive code that you 11 | don't want served to the client, such as code containing passwords or 12 | authentication mechanisms, should be kept in the `server` directory. 13 | 14 | {% apibox "Meteor.isCordova" %} 15 | {% apibox "Meteor.isDevelopment" %} 16 | {% apibox "Meteor.isProduction" %} 17 | 18 | {% apibox "Meteor.startup" %} 19 | 20 | On a server, the function will run as soon as the server process is 21 | finished starting. On a client, the function will run as soon as the DOM 22 | is ready. Code wrapped in `Meteor.startup` always runs after all app 23 | files have loaded, so you should put code here if you want to access 24 | shared variables from other files. 25 | 26 | The `startup` callbacks are called in the same order as the calls to 27 | `Meteor.startup` were made. 28 | 29 | On a client, `startup` callbacks from packages will be called 30 | first, followed by `` templates from your `.html` files, 31 | followed by your application code. 32 | 33 | ```js 34 | // On server startup, if the database is empty, create some initial data. 35 | if (Meteor.isServer) { 36 | Meteor.startup(() => { 37 | if (Rooms.find().count() === 0) { 38 | Rooms.insert({ name: 'Initial room' }); 39 | } 40 | }); 41 | } 42 | ``` 43 | 44 | {% apibox "Meteor.wrapAsync" %} 45 | 46 | {% apibox "Meteor.defer" %} 47 | 48 | {% apibox "Meteor.absoluteUrl" %} 49 | 50 | {% apibox "Meteor.settings" %} 51 | 52 | {% apibox "Meteor.release" %} 53 | -------------------------------------------------------------------------------- /source/api/ejson.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: EJSON 3 | description: Documentation of EJSON, Meteor's JSON extension. 4 | --- 5 | 6 | EJSON is an extension of JSON to support more types. It supports all JSON-safe 7 | types, as well as: 8 | 9 | - **Date** (JavaScript `Date`) 10 | - **Binary** (JavaScript `Uint8Array` or the 11 | result of [`EJSON.newBinary`](#ejson_new_binary)) 12 | - **User-defined types** (see [`EJSON.addType`](#ejson_add_type). For example, 13 | [`Mongo.ObjectID`](#mongo_object_id) is implemented this way.) 14 | 15 | All EJSON serializations are also valid JSON. For example an object with a date 16 | and a binary buffer would be serialized in EJSON as: 17 | 18 | ```json 19 | { 20 | "d": { "$date": 1358205756553 }, 21 | "b": { "$binary": "c3VyZS4=" } 22 | } 23 | ``` 24 | 25 | Meteor supports all built-in EJSON data types in publishers, method arguments 26 | and results, Mongo databases, and [`Session`](#session) variables. 27 | 28 | {% apibox "EJSON.parse" %} 29 | 30 | {% apibox "EJSON.stringify" %} 31 | 32 | {% apibox "EJSON.fromJSONValue" %} 33 | 34 | {% apibox "EJSON.toJSONValue" %} 35 | 36 | {% apibox "EJSON.equals" %} 37 | 38 | {% apibox "EJSON.clone" %} 39 | 40 | {% apibox "EJSON.newBinary" %} 41 | 42 | Buffers of binary data are represented by `Uint8Array` instances on JavaScript 43 | platforms that support them. On implementations of JavaScript that do not 44 | support `Uint8Array`, binary data buffers are represented by standard arrays 45 | containing numbers ranging from 0 to 255, and the `$Uint8ArrayPolyfill` key 46 | set to `true`. 47 | 48 | {% apibox "EJSON.isBinary" %} 49 | 50 | {% apibox "EJSON.addType" %} 51 | 52 | The factory function passed to the `EJSON.addType` method should create an instance of our custom type and initialize it with values from an object passed as the first argument of the factory function. Here is an example: 53 | 54 | ```js 55 | class Distance { 56 | constructor(value, unit) { 57 | this.value = value; 58 | this.unit = unit; 59 | } 60 | 61 | // Convert our type to JSON. 62 | toJSONValue() { 63 | return { 64 | value: this.value, 65 | unit: this.unit 66 | }; 67 | } 68 | 69 | // Unique type name. 70 | typeName() { 71 | return 'Distance'; 72 | } 73 | } 74 | 75 | EJSON.addType('Distance', function fromJSONValue(json) { 76 | return new Distance(json.value, json.unit); 77 | }); 78 | 79 | EJSON.stringify(new Distance(10, 'm')); 80 | // Returns '{"$type":"Distance","$value":{"value":10,"unit":"m"}}' 81 | ``` 82 | 83 | When you add a type to EJSON, Meteor will be able to use that type in: 84 | 85 | - publishing objects of your type if you pass them to publish handlers. 86 | - allowing your type in the return values or arguments to 87 | [methods](#methods_header). 88 | - storing your type client-side in Minimongo. 89 | - allowing your type in [`Session`](#session) variables. 90 | 91 | Instances of your type must implement [`typeName`](#ejson_type_typeName) and 92 | [`toJSONValue`](#ejson_type_toJSONValue) methods, and may implement 93 | [`clone`](#ejson_type_clone) and [`equals`](#ejson_type_equals) methods if the 94 | default implementations are not sufficient. 95 | 96 | {% apibox "EJSON.CustomType#typeName" %} 97 | {% apibox "EJSON.CustomType#toJSONValue" %} 98 | 99 | For example, the `toJSONValue` method for 100 | [`Mongo.ObjectID`](#mongo_object_id) could be: 101 | 102 | ```js 103 | function () { 104 | return this.toHexString(); 105 | } 106 | ``` 107 | 108 | {% apibox "EJSON.CustomType#clone" %} 109 | 110 | If your type does not have a `clone` method, `EJSON.clone` will use 111 | [`toJSONValue`](#ejson_type_toJSONValue) and the factory instead. 112 | 113 | {% apibox "EJSON.CustomType#equals" %} 114 | 115 | The `equals` method should define an [equivalence 116 | relation](http://en.wikipedia.org/wiki/Equivalence_relation). It should have 117 | the following properties: 118 | 119 | - *Reflexivity* - for any instance `a`: `a.equals(a)` must be true. 120 | - *Symmetry* - for any two instances `a` and `b`: `a.equals(b)` if and only if `b.equals(a)`. 121 | - *Transitivity* - for any three instances `a`, `b`, and `c`: `a.equals(b)` and `b.equals(c)` implies `a.equals(c)`. 122 | 123 | If your type does not have an `equals` method, `EJSON.equals` will compare the 124 | result of calling [`toJSONValue`](#ejson_type_toJSONValue) instead. 125 | -------------------------------------------------------------------------------- /source/api/email.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Email 3 | description: Documentation of Meteor's email API. 4 | --- 5 | 6 | The `email` package allows sending email from a Meteor app. To use it, add the 7 | package to your project by running in your terminal: 8 | 9 | ```bash 10 | meteor add email 11 | ``` 12 | 13 | The server reads from the `MAIL_URL` environment variable to determine how to 14 | send mail. The `MAIL_URL` should reference an 15 | [SMTP](https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol) server and 16 | use the form `smtp://USERNAME:PASSWORD@HOST:PORT` or 17 | `smtps://USERNAME:PASSWORD@HOST:PORT`. The `smtps://` form (the `s` is for 18 | "secure") should be used if the mail server requires TLS/SSL (and does not use 19 | `STARTTLS`) and is most common on port 465. Connections which start unencrypted 20 | prior to being upgraded to TLS/SSL (using `STARTTLS`) typically use port 587 21 | (and _sometimes_ 25) and should use `smtp://`. For more information see the 22 | [Nodemailer docs](https://nodemailer.com/smtp/) 23 | 24 | If `MAIL_URL` is not set, `Email.send` outputs the message to standard output 25 | instead. 26 | 27 | {% apibox "Email.send" %} 28 | 29 | You must provide the `from` option and at least one of `to`, `cc`, and `bcc`; 30 | all other options are optional. 31 | 32 | `Email.send` only works on the server. Here is an example of how a 33 | client could use a server method call to send an email. (In an actual 34 | application, you'd need to be careful to limit the emails that a 35 | client could send, to prevent your server from being used as a relay 36 | by spammers.) 37 | 38 | ```js 39 | // Server: Define a method that the client can call. 40 | Meteor.methods({ 41 | sendEmail(to, from, subject, text) { 42 | // Make sure that all arguments are strings. 43 | check([to, from, subject, text], [String]); 44 | 45 | // Let other method calls from the same client start running, without 46 | // waiting for the email sending to complete. 47 | this.unblock(); 48 | 49 | Email.send({ to, from, subject, text }); 50 | } 51 | }); 52 | 53 | // Client: Asynchronously send an email. 54 | Meteor.call( 55 | 'sendEmail', 56 | 'Alice ', 57 | 'bob@example.com', 58 | 'Hello from Meteor!', 59 | 'This is a test of Email.send.' 60 | ); 61 | ``` 62 | -------------------------------------------------------------------------------- /source/api/http.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: HTTP 3 | description: Documentation of Meteor's HTTP API. 4 | --- 5 | 6 | `HTTP` provides an HTTP request API on the client and server. To use 7 | these functions, add the HTTP package to your project by running in your 8 | terminal: 9 | 10 | ```bash 11 | meteor add http 12 | ``` 13 | 14 | {% apibox "HTTP.call" %} 15 | 16 | This function initiates an HTTP request to a remote server. 17 | 18 | On the server, this function can be run either synchronously or 19 | asynchronously. If the callback is omitted, it runs synchronously 20 | and the results are returned once the request completes successfully. 21 | If the request was not successful, an error is thrown. 22 | This is 23 | useful when making server-to-server HTTP API calls from within Meteor 24 | methods, as the method can succeed or fail based on the results of the 25 | synchronous HTTP call. In this case, consider using 26 | [`this.unblock()`](#method_unblock) to allow other methods on the same 27 | connection to run in 28 | the mean time. 29 | 30 | On the client, this function must be used asynchronously by passing a 31 | callback. Note that some browsers first send an `OPTIONS` request before 32 | sending your request (in order to 33 | [determine CORS headers](http://stackoverflow.com/a/21783145/627729)). 34 | 35 | Both HTTP and HTTPS protocols are supported. The `url` argument must be 36 | an absolute URL including protocol and host name on the server, but may be 37 | relative to the current host on the client. The `query` option 38 | replaces the query string of `url`. Parameters specified in `params` 39 | that are put in the URL are appended to any query string. 40 | For example, with a `url` of `'/path?query'` and 41 | `params` of `{ foo: 'bar' }`, the final URL will be `'/path?query&foo=bar'`. 42 | 43 | The `params` are put in the URL or the request body, depending on the 44 | type of request. In the case of request with no bodies, like GET and 45 | HEAD, the parameters will always go in the URL. For a POST or other 46 | type of request, the parameters will be encoded into the body with a 47 | standard `x-www-form-urlencoded` content type, unless the `content` 48 | or `data` option is used to specify a body, in which case the 49 | parameters will be appended to the URL instead. 50 | 51 | When run in asynchronous mode, the callback receives two arguments, 52 | `error` and `result`. The 53 | `error` argument will contain an Error if the request fails in any 54 | way, including a network error, time-out, or an HTTP status code in 55 | the 400 or 500 range. In case of a 4xx/5xx HTTP status code, the 56 | `response` property on `error` matches the contents of the result 57 | object. When run in synchronous mode, either `result` is returned 58 | from the function, or `error` is thrown. 59 | 60 | Contents of the result object: 61 | 62 |
63 | 64 |
statusCode 65 | Number
66 |
Numeric HTTP result status code, or null on error.
67 | 68 |
content 69 | String
70 |
The body of the HTTP response as a string.
71 | 72 |
data 73 | Object or null
74 |
If the response headers indicate JSON content, this contains the body of the document parsed as a JSON object.
75 | 76 |
headers 77 | Object
78 |
A dictionary of HTTP headers from the response.
79 | 80 |
81 | 82 | Example server method: 83 | 84 | ```js 85 | Meteor.methods({ 86 | checkTwitter(userId) { 87 | check(userId, String); 88 | this.unblock(); 89 | 90 | try { 91 | const result = HTTP.call('GET', 'http://api.twitter.com/xyz', { 92 | params: { user: userId } 93 | }); 94 | 95 | return true; 96 | } catch (e) { 97 | // Got a network error, timeout, or HTTP error in the 400 or 500 range. 98 | return false; 99 | } 100 | } 101 | }); 102 | ``` 103 | 104 | Example asynchronous HTTP call: 105 | 106 | ```js 107 | HTTP.call('POST', 'http://api.twitter.com/xyz', { 108 | data: { some: 'json', stuff: 1 } 109 | }, (error, result) => { 110 | if (!error) { 111 | Session.set('twizzled', true); 112 | } 113 | }); 114 | ``` 115 | 116 | {% apibox "HTTP.get" %} 117 | {% apibox "HTTP.post" %} 118 | {% apibox "HTTP.put" %} 119 | {% apibox "HTTP.del" %} 120 | -------------------------------------------------------------------------------- /source/api/methods.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Methods 3 | description: Documentation of Meteor's Method (Remote Procedure Call) API. 4 | --- 5 | 6 | Methods are remote functions that Meteor clients can invoke with [`Meteor.call`](#Meteor-call). 7 | 8 | {% apibox "Meteor.methods" %} 9 | 10 | Example: 11 | 12 | ```js 13 | Meteor.methods({ 14 | foo(arg1, arg2) { 15 | check(arg1, String); 16 | check(arg2, [Number]); 17 | 18 | // Do stuff... 19 | 20 | if (/* you want to throw an error */) { 21 | throw new Meteor.Error('pants-not-found', "Can't find my pants"); 22 | } 23 | 24 | return 'some return value'; 25 | }, 26 | 27 | bar() { 28 | // Do other stuff... 29 | return 'baz'; 30 | } 31 | }); 32 | ``` 33 | 34 | Calling `methods` on the server defines functions that can be called remotely by 35 | clients. They should return an [EJSON](#ejson)-able value or throw an 36 | exception. Inside your method invocation, `this` is bound to a method 37 | invocation object, which provides the following: 38 | 39 | * `isSimulation`: a boolean value, true if this invocation is a stub. 40 | * `unblock`: when called, allows the next method from this client to 41 | begin running. 42 | * `userId`: the id of the current user. 43 | * `setUserId`: a function that associates the current client with a user. 44 | * `connection`: on the server, the [connection](#meteor_onconnection) this method call was received on. 45 | 46 | Calling `methods` on the client defines *stub* functions associated with 47 | server methods of the same name. You don't have to define a stub for 48 | your method if you don't want to. In that case, method calls are just 49 | like remote procedure calls in other systems, and you'll have to wait 50 | for the results from the server. 51 | 52 | If you do define a stub, when a client invokes a server method it will 53 | also run its stub in parallel. On the client, the return value of a 54 | stub is ignored. Stubs are run for their side-effects: they are 55 | intended to *simulate* the result of what the server's method will do, 56 | but without waiting for the round trip delay. If a stub throws an 57 | exception it will be logged to the console. 58 | 59 | You use methods all the time, because the database mutators 60 | ([`insert`](#insert), [`update`](#update), [`remove`](#remove)) are implemented 61 | as methods. When you call any of these functions on the client, you're invoking 62 | their stub version that update the local cache, and sending the same write 63 | request to the server. When the server responds, the client updates the local 64 | cache with the writes that actually occurred on the server. 65 | 66 | You don't have to put all your method definitions into a single `Meteor.methods` 67 | call; you may call it multiple times, as long as each method has a unique name. 68 | 69 | If a client calls a method and is disconnected before it receives a response, 70 | it will re-call the method when it reconnects. This means that a client may 71 | call a method multiple times when it only means to call it once. If this 72 | behavior is problematic for your method, consider attaching a unique ID 73 | to each method call on the client, and checking on the server whether a call 74 | with this ID has already been made. Alternatively, you can use 75 | [`Meteor.apply`](#meteor_apply) with the noRetry option set to true. 76 | 77 | Read more about methods and how to use them in the [Methods](http://guide.meteor.com/methods.html) article in the Meteor Guide. 78 | 79 | {% apibox "DDPCommon.MethodInvocation#userId" %} 80 | 81 | The user id is an arbitrary string — typically the id of the user record 82 | in the database. You can set it with the `setUserId` function. If you're using 83 | the [Meteor accounts system](#accounts_api) then this is handled for you. 84 | 85 | {% apibox "DDPCommon.MethodInvocation#setUserId" %} 86 | 87 | Call this function to change the currently logged-in user on the 88 | connection that made this method call. This simply sets the value of 89 | `userId` for future method calls received on this connection. Pass 90 | `null` to log out the connection. 91 | 92 | If you are using the [built-in Meteor accounts system](#accounts_api) then this 93 | should correspond to the `_id` field of a document in the 94 | [`Meteor.users`](#meteor_users) collection. 95 | 96 | `setUserId` is not retroactive. It affects the current method call and 97 | any future method calls on the connection. Any previous method calls on 98 | this connection will still see the value of `userId` that was in effect 99 | when they started. 100 | 101 | If you also want to change the logged-in user on the client, then after calling 102 | `setUserId` on the server, call `Meteor.connection.setUserId(userId)` on the 103 | client. 104 | 105 | {% apibox "DDPCommon.MethodInvocation#isSimulation" %} 106 | 107 | {% apibox "DDPCommon.MethodInvocation#unblock" %} 108 | 109 | On the server, methods from a given client run one at a time. The N+1th 110 | invocation from a client won't start until the Nth invocation 111 | returns. However, you can change this by calling `this.unblock`. This 112 | will allow the N+1th invocation to start running in a new fiber. 113 | 114 | {% apibox "DDPCommon.MethodInvocation#connection" %} 115 | 116 | {% apibox "Meteor.Error" %} 117 | 118 | If you want to return an error from a method, throw an exception. Methods can 119 | throw any kind of exception. But `Meteor.Error` is the only kind of error that 120 | a server will send to the client. If a method function throws a different 121 | exception, then it will be mapped to a sanitized version on the 122 | wire. Specifically, if the `sanitizedError` field on the thrown error is set to 123 | a `Meteor.Error`, then that error will be sent to the client. Otherwise, if no 124 | sanitized version is available, the client gets 125 | `Meteor.Error(500, 'Internal server error')`. 126 | 127 | {% apibox "Meteor.call" %} 128 | 129 | This is how to invoke a method. It will run the method on the server. If a 130 | stub is available, it will also run the stub on the client. (See also 131 | [`Meteor.apply`](#meteor_apply), which is identical to `Meteor.call` except that 132 | you specify the parameters as an array instead of as separate arguments and you 133 | can specify a few options controlling how the method is executed.) 134 | 135 | If you include a callback function as the last argument (which can't be 136 | an argument to the method, since functions aren't serializable), the 137 | method will run asynchronously: it will return nothing in particular and 138 | will not throw an exception. When the method is complete (which may or 139 | may not happen before `Meteor.call` returns), the callback will be 140 | called with two arguments: `error` and `result`. If an error was thrown, 141 | then `error` will be the exception object. Otherwise, `error` will be 142 | `undefined` and the return value (possibly `undefined`) will be in `result`. 143 | 144 | ```js 145 | // Asynchronous call 146 | Meteor.call('foo', 1, 2, (error, result) => { ... }); 147 | ``` 148 | 149 | If you do not pass a callback on the server, the method invocation will 150 | block until the method is complete. It will eventually return the 151 | return value of the method, or it will throw an exception if the method 152 | threw an exception. (Possibly mapped to 500 Server Error if the 153 | exception happened remotely and it was not a `Meteor.Error` exception.) 154 | 155 | ```js 156 | // Synchronous call 157 | const result = Meteor.call('foo', 1, 2); 158 | ``` 159 | 160 | On the client, if you do not pass a callback and you are not inside a 161 | stub, `call` will return `undefined`, and you will have no way to get 162 | the return value of the method. That is because the client doesn't have 163 | fibers, so there is not actually any way it can block on the remote 164 | execution of a method. 165 | 166 | Finally, if you are inside a stub on the client and call another 167 | method, the other method is not executed (no RPC is generated, nothing 168 | "real" happens). If that other method has a stub, that stub stands in 169 | for the method and is executed. The method call's return value is the 170 | return value of the stub function. The client has no problem executing 171 | a stub synchronously, and that is why it's okay for the client to use 172 | the synchronous `Meteor.call` form from inside a method body, as 173 | described earlier. 174 | 175 | Meteor tracks the database writes performed by methods, both on the client and 176 | the server, and does not invoke `asyncCallback` until all of the server's writes 177 | replace the stub's writes in the local cache. In some cases, there can be a lag 178 | between the method's return value being available and the writes being visible: 179 | for example, if another method still outstanding wrote to the same document, the 180 | local cache may not be up to date until the other method finishes as well. If 181 | you want to process the method's result as soon as it arrives from the server, 182 | even if the method's writes are not available yet, you can specify an 183 | `onResultReceived` callback to [`Meteor.apply`](#meteor_apply). 184 | 185 | {% apibox "Meteor.apply" %} 186 | 187 | `Meteor.apply` is just like `Meteor.call`, except that the method arguments are 188 | passed as an array rather than directly as arguments, and you can specify 189 | options about how the client executes the method. 190 | 191 |

DDPRateLimiter

192 | 193 | Customize rate limiting for methods and subscriptions to avoid a high load of WebSocket messages in your app. 194 | 195 | > Galaxy (Meteor hosting) offers additional App Protection, [read more](https://galaxy-guide.meteor.com/protection.html) and try it with our [free 30-day trial](https://www.meteor.com/hosting). 196 | 197 | By default, `DDPRateLimiter` is configured with a single rule. This rule 198 | limits login attempts, new user creation, and password resets to 5 attempts 199 | every 10 seconds per connection. It can be removed by calling 200 | `Accounts.removeDefaultRateLimit()`. 201 | 202 | To use `DDPRateLimiter` for modifying the default rate-limiting rules, 203 | add the `ddp-rate-limiter` package to your project in your terminal: 204 | 205 | ```bash 206 | meteor add ddp-rate-limiter 207 | ``` 208 | 209 | {% apibox "DDPRateLimiter.addRule" nested:true instanceDelimiter:. %} 210 | 211 | Custom rules can be added by calling `DDPRateLimiter.addRule`. The rate 212 | limiter is called on every method and subscription invocation. 213 | 214 | A rate limit is reached when a bucket has surpassed the rule's predefined 215 | capactiy, at which point errors will be returned for that input until the 216 | buckets are reset. Buckets are regularly reset after the end of a time 217 | interval. 218 | 219 | 220 | Here's example of defining a rule and adding it into the `DDPRateLimiter`: 221 | ```js 222 | // Define a rule that matches login attempts by non-admin users. 223 | const loginRule = { 224 | userId(userId) { 225 | const user = Meteor.users.findOne(userId); 226 | return user && user.type !== 'admin'; 227 | }, 228 | 229 | type: 'method', 230 | name: 'login' 231 | }; 232 | 233 | // Add the rule, allowing up to 5 messages every 1000 milliseconds. 234 | DDPRateLimiter.addRule(loginRule, 5, 1000); 235 | ``` 236 | {% apibox "DDPRateLimiter.removeRule" nested:true instanceDelimiter:. %} 237 | {% apibox "DDPRateLimiter.setErrorMessage" nested:true instanceDelimiter:. %} 238 | -------------------------------------------------------------------------------- /source/api/mobile-config.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Mobile Configuration 3 | description: Documentation of Meteor's Cordova configuration API. 4 | --- 5 | 6 | If your Meteor application targets mobile platforms such as iOS or 7 | Android, you can configure your app's metadata and build process 8 | in a special top-level file called 9 | `mobile-config.js` which is *not* included in your application and is used only 10 | for this configuration. 11 | 12 | The code snippet below is an example `mobile-config.js` file. The rest of this 13 | section will explain the specific API commands in greater detail. 14 | 15 | ```js 16 | // This section sets up some basic app metadata, the entire section is optional. 17 | App.info({ 18 | id: 'com.example.matt.uber', 19 | name: 'über', 20 | description: 'Get über power in one button click', 21 | author: 'Matt Development Group', 22 | email: 'contact@example.com', 23 | website: 'http://example.com' 24 | }); 25 | 26 | // Set up resources such as icons and launch screens. 27 | App.icons({ 28 | 'iphone_2x': 'icons/icon-60@2x.png', 29 | 'iphone_3x': 'icons/icon-60@3x.png', 30 | // More screen sizes and platforms... 31 | }); 32 | 33 | App.launchScreens({ 34 | 'iphone_2x': 'splash/Default@2x~iphone.png', 35 | 'iphone5': 'splash/Default~iphone5.png', 36 | // More screen sizes and platforms... 37 | }); 38 | 39 | // Set PhoneGap/Cordova preferences. 40 | App.setPreference('BackgroundColor', '0xff0000ff'); 41 | App.setPreference('HideKeyboardFormAccessoryBar', true); 42 | App.setPreference('Orientation', 'default'); 43 | App.setPreference('Orientation', 'all', 'ios'); 44 | 45 | // Pass preferences for a particular PhoneGap/Cordova plugin. 46 | App.configurePlugin('com.phonegap.plugins.facebookconnect', { 47 | APP_ID: '1234567890', 48 | API_KEY: 'supersecretapikey' 49 | }); 50 | 51 | // Add custom tags for a particular PhoneGap/Cordova plugin to the end of the 52 | // generated config.xml. 'Universal Links' is shown as an example here. 53 | App.appendToConfig(` 54 | 55 | 56 | 57 | `); 58 | ``` 59 | 60 | {% apibox "App.info" %} 61 | {% apibox "App.setPreference" %} 62 | {% apibox "App.accessRule" %} 63 | 64 | For example this Cordova whitelist syntax: 65 | 66 | ```xml 67 | 68 | 69 | ``` 70 | 71 | is equivalent to: 72 | 73 | ```js 74 | App.accessRule('https://www.google-analytics.com'); 75 | App.accessRule('https://example.com', { type: 'navigation' }); 76 | ``` 77 | 78 | {% apibox "App.configurePlugin" %} 79 | 80 | > Note: When using `App.configurePlugin` to re-configure a plugin which has been previously configured, the changes may not be reflected without manually clearing the existing Cordova build. To clear the existing Cordova build, remove the `.meteor/local/cordova-build` directory and re-build the application using either `meteor run` or `meteor build`. 81 | 82 | {% apibox "App.icons" %} 83 | {% apibox "App.launchScreens" %} 84 | {% apibox "App.appendToConfig" %} 85 | {% apibox "App.addResourceFile" %} 86 | 87 | > Note: The resource file is copied in two steps : from the **src** of your meteor project to the root of the cordova project, then to the **target** -------------------------------------------------------------------------------- /source/api/passwords.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Passwords 3 | description: Documentation of Meteor's password-based accounts API. 4 | --- 5 | 6 | The `accounts-password` package contains a full system for password-based 7 | authentication. In addition to the basic username and password-based 8 | sign-in process, it also supports email-based sign-in including 9 | address verification and password recovery emails. 10 | 11 | The Meteor server stores passwords using the 12 | [bcrypt](http://en.wikipedia.org/wiki/Bcrypt) algorithm. This helps 13 | protect against embarrassing password leaks if the server's database is 14 | compromised. 15 | 16 | To add password support to your application, run this command in your terminal: 17 | 18 | ```bash 19 | meteor add accounts-password 20 | ``` 21 | 22 | > In addition to configuring the [`email`](email.html) package's `MAIL_URL`, it is critical that you set proper values (specifically the `from` address) in [`Accounts.emailTemplates`](#Accounts-emailTemplates) to ensure proper delivery of e-mails! 23 | 24 | You can construct your own user interface using the 25 | functions below, or use the [`accounts-ui` package](#accountsui) to 26 | include a turn-key user interface for password-based sign-in. 27 | 28 | {% apibox "Accounts.createUser" %} 29 | 30 | On the client, this function logs in as the newly created user on 31 | successful completion. On the server, it returns the newly created user 32 | id. 33 | 34 | On the client, you must pass `password` and at least one of `username` or `email` — enough information for the user to be able to log in again later. If there are existing users with a username or email only differing in case, `createUser` will fail. The callback's `error.reason` will be `'Username already exists.'` or `'Email already exists.'` In the latter case, the user can then either [login](accounts.html#Meteor-loginWithPassword) or [reset their password](#Accounts-resetPassword). 35 | 36 | On the server, you do not need to specify `password`, but the user will not be able to log in until it has a password (eg, set with [`Accounts.setPassword`](#accounts_setpassword)). To create an account without a password on the server and still let the user pick their own password, call `createUser` with the `email` option and then call [`Accounts.sendEnrollmentEmail`](#accounts_sendenrollmentemail). This will send the user an email with a link to set their initial password. 37 | 38 | By default the `profile` option is added directly to the new user document. To 39 | override this behavior, use [`Accounts.onCreateUser`](#accounts_oncreateuser). 40 | 41 | This function is only used for creating users with passwords. The external 42 | service login flows do not use this function. 43 | 44 | Instead of modifying documents in the [`Meteor.users`](#meteor_users) collection 45 | directly, use these convenience functions which correctly check for case 46 | insensitive duplicates before updates. 47 | 48 | {% apibox "Accounts.setUsername" %} 49 | 50 | {% apibox "Accounts.addEmail" %} 51 | 52 | By default, an email address is added with `{ verified: false }`. Use 53 | [`Accounts.sendVerificationEmail`](#Accounts-sendVerificationEmail) to send an 54 | email with a link the user can use to verify their email address. 55 | 56 | {% apibox "Accounts.removeEmail" %} 57 | 58 | {% apibox "Accounts.verifyEmail" %} 59 | 60 | This function accepts tokens passed into the callback registered with 61 | [`Accounts.onEmailVerificationLink`](#Accounts-onEmailVerificationLink). 62 | 63 | {% apibox "Accounts.findUserByUsername" %} 64 | 65 | {% apibox "Accounts.findUserByEmail" %} 66 | 67 | Use the below functions to initiate password changes or resets from the server 68 | or the client. 69 | 70 | {% apibox "Accounts.changePassword" %} 71 | 72 | {% apibox "Accounts.forgotPassword" %} 73 | 74 | This triggers a call 75 | to [`Accounts.sendResetPasswordEmail`](#accounts_sendresetpasswordemail) 76 | on the server. When the user visits the link in this email, the callback 77 | registered with [`Accounts.onResetPasswordLink`](#Accounts-onResetPasswordLink) 78 | will be called. 79 | 80 | If you are using the [`accounts-ui` package](#accountsui), this is handled 81 | automatically. Otherwise, it is your responsibility to prompt the user for the 82 | new password and call `resetPassword`. 83 | 84 | {% apibox "Accounts.resetPassword" %} 85 | 86 | This function accepts tokens passed into the callbacks registered with 87 | [`AccountsClient#onResetPasswordLink`](#Accounts-onResetPasswordLink) and 88 | [`Accounts.onEnrollmentLink`](#Accounts-onEnrollmentLink). 89 | 90 | {% apibox "Accounts.setPassword" %} 91 | 92 | {% apibox "Accounts.sendResetPasswordEmail" %} 93 | 94 | When the user visits the link in this email, the callback registered with 95 | [`AccountsClient#onResetPasswordLink`](#Accounts-onResetPasswordLink) will be called. 96 | 97 | To customize the contents of the email, see 98 | [`Accounts.emailTemplates`](#accounts_emailtemplates). 99 | 100 | {% apibox "Accounts.sendEnrollmentEmail" %} 101 | 102 | When the user visits the link in this email, the callback registered with 103 | [`Accounts.onEnrollmentLink`](#Accounts-onEnrollmentLink) will be called. 104 | 105 | To customize the contents of the email, see 106 | [`Accounts.emailTemplates`](#accounts_emailtemplates). 107 | 108 | {% apibox "Accounts.sendVerificationEmail" %} 109 | 110 | When the user visits the link in this email, the callback registered with 111 | [`Accounts.onEmailVerificationLink`](#Accounts-onEmailVerificationLink) will 112 | be called. 113 | 114 | To customize the contents of the email, see 115 | [`Accounts.emailTemplates`](#accounts_emailtemplates). 116 | 117 | 118 | {% apibox "Accounts.onResetPasswordLink" %} 119 | 120 | {% apibox "Accounts.onEnrollmentLink" %} 121 | 122 | {% apibox "Accounts.onEmailVerificationLink" %} 123 | 124 | {% apibox "Accounts.emailTemplates" %} 125 | 126 | This is an `Object` with several fields that are used to generate text/html 127 | for the emails sent by `sendResetPasswordEmail`, `sendEnrollmentEmail`, 128 | and `sendVerificationEmail`. 129 | 130 | Set the fields of the object by assigning to them: 131 | 132 | - `from`: (**required**) A `String` with an [RFC5322](http://tools.ietf.org/html/rfc5322) From 133 | address. By default, the email is sent from `no-reply@example.com`. **If you 134 | want e-mails to send correctly, this should be changed to your own domain 135 | as most e-mail providers will reject mail sent from `example.com`.** 136 | - `siteName`: The public name of your application. Defaults to the DNS name of 137 | the application (eg: `awesome.meteor.com`). 138 | - `headers`: An `Object` for custom email headers as described in 139 | [`Email.send`](#email_send). 140 | - `resetPassword`: An `Object` with the fields: 141 | - `from`: A `Function` used to override the `from` address defined 142 | by the `emailTemplates.from` field. 143 | - `subject`: A `Function` that takes a user object and returns 144 | a `String` for the subject line of a reset password email. 145 | - `text`: An optional `Function` that takes a user object and a url, and 146 | returns the body text for a reset password email. 147 | - `html`: An optional `Function` that takes a user object and a 148 | url, and returns the body html for a reset password email. 149 | - `enrollAccount`: Same as `resetPassword`, but for initial password setup for 150 | new accounts. 151 | - `verifyEmail`: Same as `resetPassword`, but for verifying the users email 152 | address. 153 | 154 | Example: 155 | 156 | ```js 157 | Accounts.emailTemplates.siteName = 'AwesomeSite'; 158 | Accounts.emailTemplates.from = 'AwesomeSite Admin '; 159 | 160 | Accounts.emailTemplates.enrollAccount.subject = (user) => { 161 | return `Welcome to Awesome Town, ${user.profile.name}`; 162 | }; 163 | 164 | Accounts.emailTemplates.enrollAccount.text = (user, url) => { 165 | return 'You have been selected to participate in building a better future!' 166 | + ' To activate your account, simply click the link below:\n\n' 167 | + url; 168 | }; 169 | 170 | Accounts.emailTemplates.resetPassword.from = () => { 171 | // Overrides the value set in `Accounts.emailTemplates.from` when resetting 172 | // passwords. 173 | return 'AwesomeSite Password Reset '; 174 | }; 175 | Accounts.emailTemplates.verifyEmail = { 176 | subject() { 177 | return "Activate your account now!"; 178 | }, 179 | text(user, url) { 180 | return `Hey ${user}! Verify your e-mail by following this link: ${url}`; 181 | } 182 | }; 183 | ``` 184 | -------------------------------------------------------------------------------- /source/api/pubsub.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Publish and subscribe 3 | description: Documentation of Meteor's publication and subscription API. 4 | --- 5 | 6 | These functions control how Meteor servers publish sets of records and 7 | how clients can subscribe to those sets. 8 | 9 | {% apibox "Meteor.publish" %} 10 | 11 | To publish records to clients, call `Meteor.publish` on the server with 12 | two parameters: the name of the record set, and a *publish function* 13 | that Meteor will call each time a client subscribes to the name. 14 | 15 | Publish functions can return a 16 | [`Collection.Cursor`](#mongo_cursor), in which case Meteor 17 | will publish that cursor's documents to each subscribed client. You can 18 | also return an array of `Collection.Cursor`s, in which case Meteor will 19 | publish all of the cursors. 20 | 21 | {% pullquote 'warning' %} 22 | If you return multiple cursors in an array, they currently must all be from 23 | different collections. We hope to lift this restriction in a future release. 24 | {% endpullquote %} 25 | 26 | A client will see a document if the document is currently in the published 27 | record set of any of its subscriptions. If multiple publications publish a 28 | document with the same `_id` to the same collection the documents will be 29 | merged for the client. If the values of any of the top level fields 30 | conflict, the resulting value will be one of the published values, chosen 31 | arbitrarily. 32 | 33 | ```js 34 | // Server: Publish the `Rooms` collection, minus secret info... 35 | Meteor.publish('rooms', function () { 36 | return Rooms.find({}, { 37 | fields: { secretInfo: 0 } 38 | }); 39 | }); 40 | 41 | // ...and publish secret info for rooms where the logged-in user is an admin. If 42 | // the client subscribes to both publications, the records are merged together 43 | // into the same documents in the `Rooms` collection. Note that currently object 44 | // values are not recursively merged, so the fields that differ must be top 45 | // level fields. 46 | Meteor.publish('adminSecretInfo', function () { 47 | return Rooms.find({ admin: this.userId }, { 48 | fields: { secretInfo: 1 } 49 | }); 50 | }); 51 | 52 | // Publish dependent documents and simulate joins. 53 | Meteor.publish('roomAndMessages', function (roomId) { 54 | check(roomId, String); 55 | 56 | return [ 57 | Rooms.find({ _id: roomId }, { 58 | fields: { secretInfo: 0 } 59 | }), 60 | Messages.find({ roomId }) 61 | ]; 62 | }); 63 | ``` 64 | 65 | Alternatively, a publish function can directly control its published record set 66 | by calling the functions [`added`](#publish_added) (to add a new document to the 67 | published record set), [`changed`](#publish_changed) (to change or clear some 68 | fields on a document already in the published record set), and 69 | [`removed`](#publish_removed) (to remove documents from the published record 70 | set). These methods are provided by `this` in your publish function. 71 | 72 | If a publish function does not return a cursor or array of cursors, it is 73 | assumed to be using the low-level `added`/`changed`/`removed` interface, and it 74 | **must also call [`ready`](#publish_ready) once the initial record set is 75 | complete**. 76 | 77 | Example (server): 78 | 79 | ```js 80 | // Publish the current size of a collection. 81 | Meteor.publish('countsByRoom', function (roomId) { 82 | check(roomId, String); 83 | 84 | let count = 0; 85 | let initializing = true; 86 | 87 | // `observeChanges` only returns after the initial `added` callbacks have run. 88 | // Until then, we don't want to send a lot of `changed` messages—hence 89 | // tracking the `initializing` state. 90 | const handle = Messages.find({ roomId }).observeChanges({ 91 | added: (id) => { 92 | count += 1; 93 | 94 | if (!initializing) { 95 | this.changed('counts', roomId, { count }); 96 | } 97 | }, 98 | 99 | removed: (id) => { 100 | count -= 1; 101 | this.changed('counts', roomId, { count }); 102 | } 103 | 104 | // We don't care about `changed` events. 105 | }); 106 | 107 | // Instead, we'll send one `added` message right after `observeChanges` has 108 | // returned, and mark the subscription as ready. 109 | initializing = false; 110 | this.added('counts', roomId, { count }); 111 | this.ready(); 112 | 113 | // Stop observing the cursor when the client unsubscribes. Stopping a 114 | // subscription automatically takes care of sending the client any `removed` 115 | // messages. 116 | this.onStop(() => handle.stop()); 117 | }); 118 | 119 | // Sometimes publish a query, sometimes publish nothing. 120 | Meteor.publish('secretData', function () { 121 | if (this.userId === 'superuser') { 122 | return SecretData.find(); 123 | } else { 124 | // Declare that no data is being published. If you leave this line out, 125 | // Meteor will never consider the subscription ready because it thinks 126 | // you're using the `added/changed/removed` interface where you have to 127 | // explicitly call `this.ready`. 128 | return []; 129 | } 130 | }); 131 | ``` 132 | 133 | Example (client): 134 | 135 | ```js 136 | // Declare a collection to hold the count object. 137 | const Counts = new Mongo.Collection('counts'); 138 | 139 | // Subscribe to the count for the current room. 140 | Tracker.autorun(() => { 141 | Meteor.subscribe('countsByRoom', Session.get('roomId')); 142 | }); 143 | 144 | // Use the new collection. 145 | const roomCount = Counts.findOne(Session.get('roomId')).count; 146 | console.log(`Current room has ${roomCount} messages.`); 147 | ``` 148 | 149 | {% pullquote 'warning' %} 150 | Meteor will emit a warning message if you call `Meteor.publish` in a 151 | project that includes the `autopublish` package. Your publish function 152 | will still work. 153 | {% endpullquote %} 154 | 155 | Read more about publications and how to use them in the 156 | [Data Loading](http://guide.meteor.com/data-loading.html) article in the Meteor Guide. 157 | 158 | {% apibox "Subscription#userId" %} 159 | 160 | This is constant. However, if the logged-in user changes, the publish 161 | function is rerun with the new value, assuming it didn't throw an error at the previous run. 162 | 163 | {% apibox "Subscription#added" %} 164 | {% apibox "Subscription#changed" %} 165 | {% apibox "Subscription#removed" %} 166 | {% apibox "Subscription#ready" %} 167 | {% apibox "Subscription#onStop" %} 168 | 169 | If you call [`observe`](#observe) or [`observeChanges`](#observe_changes) in your 170 | publish handler, this is the place to stop the observes. 171 | 172 | {% apibox "Subscription#error" %} 173 | {% apibox "Subscription#stop" %} 174 | {% apibox "Subscription#connection" %} 175 | 176 | {% apibox "Meteor.subscribe" %} 177 | 178 | When you subscribe to a record set, it tells the server to send records to the 179 | client. The client stores these records in local [Minimongo 180 | collections](#mongo_collection), with the same name as the `collection` 181 | argument used in the publish handler's [`added`](#publish_added), 182 | [`changed`](#publish_changed), and [`removed`](#publish_removed) 183 | callbacks. Meteor will queue incoming records until you declare the 184 | [`Mongo.Collection`](#mongo_collection) on the client with the matching 185 | collection name. 186 | 187 | ```js 188 | // It's okay to subscribe (and possibly receive data) before declaring the 189 | // client collection that will hold it. Assume 'allPlayers' publishes data from 190 | // the server's 'players' collection. 191 | Meteor.subscribe('allPlayers'); 192 | ... 193 | 194 | // The client queues incoming 'players' records until the collection is created: 195 | const Players = new Mongo.Collection('players'); 196 | ``` 197 | 198 | The client will see a document if the document is currently in the published 199 | record set of any of its subscriptions. If multiple publications publish a 200 | document with the same `_id` for the same collection the documents are merged for 201 | the client. If the values of any of the top level fields conflict, the resulting 202 | value will be one of the published values, chosen arbitrarily. 203 | 204 | {% pullquote 'warning' %} 205 | Currently, when multiple subscriptions publish the same document *only the top 206 | level fields* are compared during the merge. This means that if the documents 207 | include different sub-fields of the same top level field, not all of them will 208 | be available on the client. We hope to lift this restriction in a future release. 209 | {% endpullquote %} 210 | 211 | The `onReady` callback is called with no arguments when the server [marks the 212 | subscription as ready](#publish_ready). The `onStop` callback is called with 213 | a [`Meteor.Error`](#meteor_error) if the subscription fails or is terminated by 214 | the server. If the subscription is stopped by calling `stop` on the subscription 215 | handle or inside the publication, `onStop` is called with no arguments. 216 | 217 | `Meteor.subscribe` returns a subscription handle, which is an object with the 218 | following properties: 219 | 220 |
221 | {% dtdd name:"stop()" %} 222 | Cancel the subscription. This will typically result in the server directing the 223 | client to remove the subscription's data from the client's cache. 224 | {% enddtdd %} 225 | 226 | {% dtdd name:"ready()" %} 227 | True if the server has [marked the subscription as ready](#publish_ready). A 228 | reactive data source. 229 | {% enddtdd %} 230 | 231 | {% dtdd name:"subscriptionId" %} 232 | The `id` of the subscription this handle is for. When you run `Meteor.subscribe` 233 | inside of `Tracker.autorun`, the handles you get will always have the same 234 | `subscriptionId` field. You can use this to deduplicate subscription handles 235 | if you are storing them in some data structure. 236 | {% enddtdd %} 237 |
238 | 239 | If you call `Meteor.subscribe` within a [reactive computation](#reactivity), 240 | for example using 241 | [`Tracker.autorun`](#tracker_autorun), the subscription will automatically be 242 | cancelled when the computation is invalidated or stopped; it is not necessary 243 | to call `stop` on 244 | subscriptions made from inside `autorun`. However, if the next iteration 245 | of your run function subscribes to the same record set (same name and 246 | parameters), Meteor is smart enough to skip a wasteful 247 | unsubscribe/resubscribe. For example: 248 | 249 | ```js 250 | Tracker.autorun(() => { 251 | Meteor.subscribe('chat', { room: Session.get('currentRoom') }); 252 | Meteor.subscribe('privateMessages'); 253 | }); 254 | ``` 255 | 256 | This subscribes you to the chat messages in the current room and to your private 257 | messages. When you change rooms by calling `Session.set('currentRoom', 258 | 'newRoom')`, Meteor will subscribe to the new room's chat messages, 259 | unsubscribe from the original room's chat messages, and continue to 260 | stay subscribed to your private messages. 261 | -------------------------------------------------------------------------------- /source/api/reactive-dict.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ReactiveDict 3 | description: Documentation of ReactiveDict, a simple reactive dictionary package. 4 | --- 5 | 6 | A ReactiveDict stores an arbitrary set of key-value pairs. Use it to manage 7 | internal state in your components, ie. like the currently selected item in a list. 8 | Each key is individully reactive such that calling `set` for a key will 9 | invalidate any Computations that called `get` with that key, according to the 10 | usual contract for reactive data sources. 11 | 12 | That means if you call [`ReactiveDict#get`](#ReactiveDict-get)`('currentList')` 13 | from inside a Blaze template helper, the template will automatically be rerendered 14 | whenever [`ReactiveDict#set`](#ReactiveDict-set)`('currentList', x)` is called. 15 | 16 | To use `ReactiveDict`, add the `reactive-dict` package to your project by running 17 | in your terminal: 18 | 19 | ```bash 20 | meteor add reactive-dict 21 | ``` 22 | 23 | {% apibox "ReactiveDict" %} 24 | 25 | If you provide a name to its constructor, its contents will be saved across Hot 26 | Code Push client code updates. 27 | 28 | {% apibox "ReactiveDict#set" %} 29 | 30 | Example: 31 | 32 | ```js 33 | const state = new ReactiveDict(); 34 | state.set('currentRoomId', 'random') 35 | 36 | Tracker.autorun(() => { 37 | Meteor.subscribe('chatHistory', { room: state.get('currentRoomId') }); 38 | }); 39 | 40 | // Causes the function passed to `Tracker.autorun` to be rerun, so that the 41 | // 'chatHistory' subscription is moved to the room 'general'. 42 | state.set('currentRoomId', 'general'); 43 | ``` 44 | 45 | `ReactiveDict.set` can also be called with an object of keys and values, which is 46 | equivalent to calling `ReactiveDict.set` individually on each key/value pair. 47 | 48 | ```js 49 | const state = new ReactiveDict(); 50 | state.set({ 51 | a: 'foo', 52 | b: 'bar' 53 | }); 54 | ``` 55 | 56 | {% apibox "ReactiveDict#setDefault" %} 57 | 58 | This is useful in initialization code, to avoid re-initializing your state 59 | every time a new version of your app is loaded. 60 | 61 | {% apibox "ReactiveDict#get" %} 62 | 63 | Example: 64 | 65 | ```html 66 | 67 | 71 | ``` 72 | 73 | ```js 74 | // main.js 75 | Template.main.onCreated(function () { 76 | this.state = new ReactiveDict(); 77 | this.state.set('enemy', 'Eastasia'); 78 | }); 79 | Template.main.helpers({ 80 | theEnemy() { 81 | const inst = Template.instance(); 82 | return inst.state.get('enemy'); 83 | } 84 | }); 85 | Template.main.events({ 86 | 'click .change-enemy'(event, inst) { 87 | inst.state.set('enemy', 'Eurasia') 88 | } 89 | }); 90 | 91 | // Clicking the button will change the page to say "We've always been at war with Eurasia" 92 | ``` 93 | 94 | {% apibox "ReactiveDict#equals" %} 95 | 96 | If value is a scalar, then these two expressions do the same thing: 97 | 98 | ```js 99 | const state = new ReactiveDict() 100 | // ... 101 | state.get('key') === value 102 | state.equals('key', value) 103 | ``` 104 | 105 | However, the second is recommended, as it triggers fewer invalidations 106 | (template redraws), making your program more efficient. 107 | 108 | {% apibox "ReactiveDict#all" %} 109 | 110 | {% apibox "ReactiveDict#clear" %} 111 | 112 | {% apibox "ReactiveDict#destroy" %} 113 | -------------------------------------------------------------------------------- /source/api/reactive-var.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ReactiveVar 3 | description: Documentation of ReactiveVar, a simple reactive variable package. 4 | --- 5 | 6 | To use `ReactiveVar`, add the `reactive-var` package to your project by running 7 | in your terminal: 8 | 9 | ```bash 10 | meteor add reactive-var 11 | ``` 12 | 13 | {% apibox "ReactiveVar" %} 14 | 15 | A ReactiveVar holds a single value that can be get and set, such that calling 16 | `set` will invalidate any Computations that called `get`, according to the 17 | usual contract for reactive data sources. 18 | 19 | A ReactiveVar is similar to a Session variable, with a few differences: 20 | 21 | * ReactiveVars don't have global names, like the "foo" in `Session.get('foo')`. 22 | Instead, they may be created and used locally, for example attached to a 23 | template instance, as in: `this.foo.get()`. 24 | 25 | * ReactiveVars are not automatically migrated across hot code pushes, 26 | whereas Session state is. 27 | 28 | * ReactiveVars can hold any value, while Session variables are limited to 29 | JSON or EJSON. 30 | 31 | An important property of ReactiveVars — which is sometimes a 32 | reason for using one — is that setting the value to the same 33 | value as before has no effect; it does not trigger any invalidations. 34 | So if one autorun sets a ReactiveVar, and another autorun gets the 35 | ReactiveVar, a re-run of the first autorun won't necessarily trigger 36 | the second. By default, only primitive values are compared this way, 37 | while calling `set` on an argument that is an *object* (not a 38 | primitive) always counts as a change. You can configure this behavior 39 | using the `equalsFunc` argument. 40 | 41 | {% apibox "ReactiveVar#get" %} 42 | 43 | {% apibox "ReactiveVar#set" %} 44 | -------------------------------------------------------------------------------- /source/api/session.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Session 3 | description: Documentation of Meteor's client-side session API. 4 | --- 5 | 6 | `Session` provides a global object on the client that you can use to 7 | store an arbitrary set of key-value pairs. Use it to store things like 8 | the currently selected item in a list. 9 | 10 | What's special about `Session` is that it's reactive. If 11 | you call [`Session.get`](#session_get)`('currentList')` 12 | from inside a template, the template will automatically be rerendered 13 | whenever [`Session.set`](#session_set)`('currentList', x)` is called. 14 | 15 | To add `Session` to your application, run this command in your terminal: 16 | 17 | ```bash 18 | meteor add session 19 | ``` 20 | 21 | {% apibox "Session.set" %} 22 | 23 | Example: 24 | 25 | ```js 26 | Tracker.autorun(() => { 27 | Meteor.subscribe('chatHistory', { room: Session.get('currentRoomId') }); 28 | }); 29 | 30 | // Causes the function passed to `Tracker.autorun` to be rerun, so that the 31 | // 'chatHistory' subscription is moved to the room 'home'. 32 | Session.set('currentRoomId', 'home'); 33 | ``` 34 | 35 | `Session.set` can also be called with an object of keys and values, which is 36 | equivalent to calling `Session.set` individually on each key/value pair. 37 | 38 | ```js 39 | Session.set({ 40 | a: 'foo', 41 | b: 'bar' 42 | }); 43 | ``` 44 | 45 | {% apibox "Session.setDefault" %} 46 | 47 | This is useful in initialization code, to avoid re-initializing a session 48 | variable every time a new version of your app is loaded. 49 | 50 | {% apibox "Session.get" %} 51 | 52 | Example: 53 | 54 | ```html 55 | 56 | 59 | ``` 60 | 61 | ```js 62 | // main.js 63 | Template.main.helpers({ 64 | theEnemy() { 65 | return Session.get('enemy'); 66 | } 67 | }); 68 | 69 | Session.set('enemy', 'Eastasia'); 70 | // Page will say "We've always been at war with Eastasia" 71 | 72 | Session.set('enemy', 'Eurasia'); 73 | // Page will change to say "We've always been at war with Eurasia" 74 | ``` 75 | 76 | 77 | {% apibox "Session.equals" %} 78 | 79 | If value is a scalar, then these two expressions do the same thing: 80 | 81 | ```js 82 | Session.get('key') === value 83 | Session.equals('key', value) 84 | ``` 85 | 86 | ...but the second one is always better. It triggers fewer invalidations 87 | (template redraws), making your program more efficient. 88 | 89 | Example: 90 | 91 | ```html 92 | 101 | 102 | 105 | ``` 106 | 107 | ```js 108 | Template.postsView.helpers({ 109 | posts() { 110 | return Posts.find(); 111 | } 112 | }); 113 | 114 | Template.postItem.helpers({ 115 | postClass() { 116 | return Session.equals('selectedPost', this._id) 117 | ? 'selected' 118 | : ''; 119 | } 120 | }); 121 | 122 | Template.postItem.events({ 123 | 'click'() { 124 | Session.set('selectedPost', this._id); 125 | } 126 | }); 127 | ``` 128 | 129 | Using Session.equals here means that when the user clicks 130 | on an item and changes the selection, only the newly selected 131 | and the newly unselected items are re-rendered. 132 | 133 | If Session.get had been used instead of Session.equals, then 134 | when the selection changed, all the items would be re-rendered. 135 | 136 | For object and array session values, you cannot use `Session.equals`; instead, 137 | you need to use the `underscore` package and write 138 | `_.isEqual(Session.get(key), value)`. 139 | -------------------------------------------------------------------------------- /source/api/templates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Templates 3 | description: Documentation of Meteor's template API. 4 | --- 5 | 6 | This documentation has moved to the [Blaze Community Site](http://blazejs.org/api/templates.html). 7 | -------------------------------------------------------------------------------- /source/api/timers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Timers 3 | description: Documentation of Meteor's timeout APIs. 4 | --- 5 | 6 | Meteor uses global environment variables 7 | to keep track of things like the current request's user. To make sure 8 | these variables have the right values, you need to use 9 | `Meteor.setTimeout` instead of `setTimeout` and `Meteor.setInterval` 10 | instead of `setInterval`. 11 | 12 | These functions work just like their native JavaScript equivalents. 13 | If you call the native function, you'll get an error stating that Meteor 14 | code must always run within a Fiber, and advising to use 15 | `Meteor.bindEnvironment`. 16 | 17 | {% apibox "Meteor.setTimeout" %} 18 | 19 | Returns a handle that can be used by `Meteor.clearTimeout`. 20 | 21 | {% apibox "Meteor.setInterval" %} 22 | 23 | Returns a handle that can be used by `Meteor.clearInterval`. 24 | 25 | {% apibox "Meteor.clearTimeout" %} 26 | {% apibox "Meteor.clearInterval" %} 27 | -------------------------------------------------------------------------------- /source/changelog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Meteor Changelog 3 | --- 4 | 5 | {%- changelog 'code/History.md' %} 6 | -------------------------------------------------------------------------------- /source/environment-variables.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Environment Variables 3 | description: List of environment variables that you can use with your Meteor application. 4 | --- 5 | 6 | Here's a list of the environment variables you can provide to your application. 7 | 8 | ## BIND_IP 9 | (_production_) 10 | 11 | Bind the application server to a specific network interface by IP address, for example: `BIND_IP=192.168.0.2`. 12 | 13 | See also: [`PORT`](#PORT). 14 | 15 | > In development, this can be accomplished with `meteor run --port a.b.c.d:port`. 16 | 17 | ## DISABLE_WEBSOCKETS 18 | (_development, production_) 19 | 20 | In the event that your own deployment platform does not support WebSockets, or you are confident that you will not benefit from them, setting this variable with `DISABLE_WEBSOCKETS=1` will explicitly disable WebSockets and forcibly resort to the fallback polling-mechanism, instead of trying to detect this automatically. 21 | 22 | ## HTTP_FORWARDED_COUNT 23 | (_production_) 24 | 25 | Set this to however many number of proxies you have running before your Meteor application. For example, if have an NGINX server acting as a proxy before your Meteor application, you would set `HTTP_FORWARDED_COUNT=1`. If you have a load balancer in front of that NGINX server, the count is 2. 26 | 27 | ## MAIL_URL 28 | (_development, production_) 29 | 30 | Use this variable to set the SMTP server for sending e-mails. [Postmark](https://www.postmarkapp.com), [Mandrill](https://www.mandrillapp.com), [MailGun](https://www.mailgun.com) and [SendGrid](https://www.sendgrid.com) (among others) are companies who can provide this service. The `MAIL_URL` contains all of the information for connecting to the SMTP server and, like a URL, should look like `smtp://user:pass@yourservice.com:587` or `smtps://user:pass@yourservice.com:465`. 31 | 32 | The `smtp://` form is for mail servers which support encryption via `STARTTLS` or those that do not use encryption at all and is most common for servers on port 587 and _sometimes_ port 25. On the other hand, the `smtps://` form (the `s` stands for "secure") should be used if the server only supports TLS/SSL (and does not support connection upgrade with `STARTTLS`) and is most common for servers on port 465. 33 | 34 | ## METEOR_DISABLE_OPTIMISTIC_CACHING 35 | (_production_) 36 | 37 | When running `meteor build` or `meteor deploy` you can set `METEOR_DISABLE_OPTIMISTIC_CACHING=1` to speed up your build time. 38 | 39 | Since optimistic in-memory caching is one of the more memory-intensive parts of the build system, setting the environment variable `METEOR_DISABLE_OPTIMISTIC_CACHING=1` can help improve memory usage during meteor build, which seems to improve the total build times. This configuration is perfectly safe because the whole point of optimistic caching is to keep track of previous results for future rebuilds, but in the case of meteor `build` or `deploy` there's only ever one initial build, so the extra bookkeeping is unnecessary. 40 | 41 | ## METEOR_PACKAGE_DIRS 42 | (_development, production_) 43 | 44 | Colon-delimited list of local package directories to look in, outside your normal application structure, for example: `METEOR_PACKAGE_DIRS="/usr/local/my_packages/"`. Note that this used to be `PACKAGE_DIRS` but was changed in Meteor 1.4.2. 45 | 46 | ## METEOR_SETTINGS 47 | (_production_) 48 | 49 | When running your bundled application in production mode, pass a string of JSON containing your settings with `METEOR_SETTINGS='{ "server_only_setting": "foo", "public": { "client_and_server_setting": "bar" } }'`. 50 | 51 | > In development, this is accomplished with `meteor --settings [file.json]` in order to provide full-reactivity when changing settings. Those settings are simply passed as a string here. Please see the [Meteor.settings](http://docs.meteor.com/api/core.html#Meteor-settings) documentation for further information. 52 | 53 | ## MONGO_OPLOG_URL 54 | (_development, production_) 55 | 56 | MongoDB server oplog URL. If you're using a replica set (which you should), construct this url like so: `MONGO_OPLOG_URL="mongodb://user:password@myserver.com:10139/local?replicaSet=(your replica set)&authSource=(your auth source)"` 57 | 58 | ## MONGO_URL 59 | (_development, production_) 60 | 61 | MongoDB server URL. Give a fully qualified URL (or comma-separated list of URLs) like `MONGO_URL="mongodb://user:password@myserver.com:10139"`. For more information see the [MongoDB docs](https://docs.mongodb.com/manual/reference/connection-string/). 62 | 63 | ## PORT 64 | (_production_) 65 | 66 | Which port the app should listen on, for example: `PORT=3030` 67 | 68 | See also: [`BIND_IP`](#BIND-IP). 69 | 70 | > In development, this can be accomplished with `meteor run --port `. 71 | 72 | ## ROOT_URL 73 | (_development, production_) 74 | 75 | Used to generate URLs to your application by, among others, the accounts package. Provide a full URL to your application like this: `ROOT_URL="https://www.myapp.com"`. 76 | 77 | ## TOOL_NODE_FLAGS 78 | (_development, production_) 79 | 80 | Used to pass flags/variables to Node inside Meteor build. For example you can use this to pass a link to icu data: `TOOL_NODE_FLAGS="--icu-data-dir=node_modules/full-icu"` 81 | For full list of available flags see the [Node documentation](https://nodejs.org/dist/latest-v12.x/docs/api/cli.html). 82 | 83 | 84 | -------------------------------------------------------------------------------- /source/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Meteor API Docs 3 | --- 4 | 5 | 6 |

What is Meteor?

7 | 8 | Meteor is a full-stack JavaScript platform for developing modern web and mobile applications. Meteor includes a key set of technologies for building connected-client reactive applications, a build tool, and a curated set of packages from the Node.js and general JavaScript community. 9 | 10 | - Meteor allows you to develop in **one language**, JavaScript, in all environments: application server, web browser, and mobile device. 11 | 12 | - Meteor uses **data on the wire**, meaning the server sends data, not HTML, and the client renders it. 13 | 14 | - Meteor **embraces the ecosystem**, bringing the best parts of the extremely active JavaScript community to you in a careful and considered way. 15 | 16 | - Meteor provides **full stack reactivity**, allowing your UI to seamlessly reflect the true state of the world with minimal development effort. 17 | 18 |

Meteor resources

19 | 20 | 1. The place to get started with Meteor is the [official tutorial](https://www.meteor.com/tutorials/blaze/creating-an-app). 21 | 22 | 2. Once you are familiar with the basics, the [Meteor Guide](http://guide.meteor.com) covers intermediate material on how to use Meteor in a larger scale app. 23 | 24 | 3. [Stack Overflow](http://stackoverflow.com/questions/tagged/meteor) is the best place to ask (and answer!) technical questions. Be sure to add the meteor tag to your question. 25 | 26 | 4. Visit the [Meteor discussion forums](https://forums.meteor.com) to announce projects, get help, talk about the community, or discuss changes to core. 27 | 28 | 5. [Atmosphere](https://atmospherejs.com) is the repository of community packages designed especially for Meteor. 29 | 30 | 6. [Awesome Meteor](https://github.com/Urigo/awesome-meteor) is a community-curated list of [packages](https://github.com/Urigo/awesome-meteor#getting-started) and [resources](https://github.com/Urigo/awesome-meteor#resources). 31 | 32 | 33 | {% oldRedirects %} 34 | 35 | 36 | -------------------------------------------------------------------------------- /source/packages/accounts-ui.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: accounts-ui 3 | description: Documentation of Meteor's `accounts-ui` package. 4 | --- 5 | 6 | A turn-key user interface for Meteor Accounts. 7 | 8 | To add Accounts and a set of login controls to an application, add the 9 | `accounts-ui` package and at least one login provider package: 10 | `accounts-password`, `accounts-facebook`, `accounts-github`, 11 | `accounts-google`, `accounts-twitter`, or `accounts-weibo`. 12 | 13 | Then simply add the `{% raw %}{{> loginButtons}}{% endraw %}` helper to an HTML file. This 14 | will place a login widget on the page. If there is only one provider configured 15 | and it is an external service, this will add a login/logout button. If you use 16 | `accounts-password` or use multiple external login services, this will add 17 | a "Sign in" link which opens a dropdown menu with login options. If you plan to 18 | position the login dropdown in the right edge of the screen, use 19 | `{% raw %}{{> loginButtons align="right"}}{% endraw %}` in order to get the dropdown to lay 20 | itself out without expanding off the edge of the screen. 21 | 22 | To configure the behavior of `{% raw %}{{> loginButtons}}{% endraw %}`, use 23 | [`Accounts.ui.config`](#accounts_ui_config). 24 | 25 | `accounts-ui` also includes modal popup dialogs to handle links from 26 | [`sendResetPasswordEmail`](#accounts_sendresetpasswordemail), [`sendVerificationEmail`](#accounts_sendverificationemail), 27 | and [`sendEnrollmentEmail`](#accounts_sendenrollmentemail). These 28 | do not have to be manually placed in HTML: they are automatically activated 29 | when the URLs are loaded. 30 | 31 | If you want to control the look and feel of your accounts system a little more, we recommend reading the [useraccounts](http://guide.meteor.com/accounts.html#useraccounts) section of the Meteor Guide. 32 | -------------------------------------------------------------------------------- /source/packages/appcache.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: appcache 3 | description: Documentation of Meteor's `appcache` package. 4 | --- 5 | 6 | The `appcache` package stores the static parts of a Meteor application 7 | (the client side Javascript, HTML, CSS, and images) in the browser's 8 | [application cache](https://en.wikipedia.org/wiki/AppCache). To enable 9 | caching simply add the `appcache` package to your project. 10 | 11 | * Once a user has visited a Meteor application for the first time and 12 | the application has been cached, on subsequent visits the web page 13 | loads faster because the browser can load the application out of the 14 | cache without contacting the server first. 15 | 16 | * Hot code pushes are loaded by the browser in the background while the 17 | app continues to run. Once the new code has been fully loaded the 18 | browser is able to switch over to the new code quickly. 19 | 20 | * The application cache allows the application to be loaded even when 21 | the browser doesn't have an Internet connection, and so enables using 22 | the app offline. 23 | 24 | (Note however that the `appcache` package by itself doesn't make 25 | *data* available offline: in an application loaded offline, a Meteor 26 | Collection will appear to be empty in the client until the Internet 27 | becomes available and the browser is able to establish a DDP 28 | connection). 29 | 30 | To turn AppCache off for specific browsers use: 31 | 32 | ```js 33 | Meteor.AppCache.config({ 34 | chrome: false, 35 | firefox: false 36 | }); 37 | ``` 38 | 39 | The supported browsers that can be enabled or disabled include, but are 40 | not limited to, `android`, `chrome`, `chromium`, `chromeMobileIOS`, 41 | `firefox`, `ie`, `mobileSafari` and `safari`. 42 | 43 | Browsers limit the amount of data they will put in the application 44 | cache, which can vary due to factors such as how much disk space is 45 | free. Unfortunately if your application goes over the limit rather 46 | than disabling the application cache altogether and running the 47 | application online, the browser will instead fail that particular 48 | *update* of the cache, leaving your users running old code. 49 | 50 | Thus it's best to keep the size of the cache below 5MB. The 51 | `appcache` package will print a warning on the Meteor server console 52 | if the total size of the resources being cached is over 5MB. 53 | 54 | Starting from `appcache@1.2.5`, if you need more advanced logic 55 | to enable/disable the cache, you can use the `enableCallback` option 56 | that is evaluated on a per-request basis. For example: 57 | 58 | ```js 59 | // Enable offline mode using a value from database and certificate validation 60 | Meteor.AppCache.config({ 61 | // This option is available starting from appcache@1.2.4 62 | enableCallback: () => { 63 | if (!getSettingsFromDb("public.appcache_enabled")) { 64 | return false; 65 | } 66 | 67 | const validation = validateClientCert({ 68 | clientCertPayload: req.headers["x-client-cert"], 69 | url: req.url.href, 70 | }); 71 | 72 | return validation.passed; 73 | }, 74 | }); 75 | ``` 76 | 77 | If you have files too large to fit in the cache you can disable 78 | caching by URL prefix. For example, 79 | 80 | ```js 81 | Meteor.AppCache.config({ onlineOnly: ['/online/'] }); 82 | ``` 83 | 84 | causes files in your `public/online` directory to not be cached, and 85 | so they will only be available online. You can then move your large 86 | files into that directory and refer to them at the new URL: 87 | 88 | ```html 89 | 90 | ``` 91 | 92 | If you'd prefer not to move your files, you can use the file names 93 | themselves as the URL prefix: 94 | 95 | ```js 96 | Meteor.AppCache.config({ 97 | onlineOnly: [ 98 | '/bigimage.jpg', 99 | '/largedata.json' 100 | ] 101 | }); 102 | ``` 103 | 104 | though keep in mind that since the exclusion is by prefix (this is a 105 | limitation of the application cache manifest), excluding 106 | `/largedata.json` will also exclude such URLs as 107 | `/largedata.json.orig` and `/largedata.json/file1`. 108 | 109 | For more information about how Meteor interacts with the application 110 | cache, see the 111 | [AppCache page](https://github.com/meteor/meteor/wiki/AppCache) 112 | in the Meteor wiki. 113 | -------------------------------------------------------------------------------- /source/packages/audit-argument-checks.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: audit-argument-checks 3 | description: Documentation of Meteor's `audit-argument-checks` package. 4 | --- 5 | 6 | This package causes Meteor to require that all arguments passed to methods and 7 | publish functions are [checked](#check). Any method that does not pass each 8 | one of its arguments to `check` will throw an error, which will be logged on the 9 | server and which will appear to the client as a 10 | `500 Internal server error`. This is a simple way to help ensure that your 11 | app has complete check coverage. 12 | 13 | Methods and publish functions that do not need to validate their arguments can 14 | simply run `check(arguments, [Match.Any])` to satisfy the 15 | `audit-argument-checks` coverage checker. 16 | -------------------------------------------------------------------------------- /source/packages/autoupdate.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: autoupdate 3 | description: Documentation of Meteor's `autoupdate` package. 4 | --- 5 | 6 | This is the Meteor package that provides hot code push (HCP) functionality. 7 | 8 | Every Meteor application that wasn't created with the `--minimal` option 9 | has this package already through `meteor-base` and HCP should work out of the 10 | box. For those running `--minimal` applications and want to benefit from this 11 | package, just add it with `meteor add autoupdate`. 12 | 13 | > `autoupdate` adds up to 30KB on your client's production bundle. 14 | 15 | With this package Meteor will use DDP to publish a collection called 16 | _'meteor_autoupdate_clientVersions'_. This collection will be subscribed by the 17 | user's client and every time the client identifies a change in the published 18 | version it will refresh itself. 19 | 20 | ## Browser Client 21 | 22 | The refresh will happen in the browser in two different ways: a _soft update_, 23 | and a _hard update_. If Meteor identifies that only stylesheets were changed, then it 24 | will verify if the user's browser is capable of reloading CSS on the fly, and a 25 | soft update will take place. The soft update will replace the old stylesheet 26 | with the new stylesheet without triggering a full page reload. 27 | 28 | In cases where a change in a server's or client's compiled file happens, the hard 29 | update will take place: Meteor will force a complete browser reload using the 30 | `reload` package. 31 | 32 | > Among other things, the `reload` package tries do reload the application 33 | > preserving some unchanged cached files. 34 | 35 | ## Cordova Client 36 | 37 | There is no soft update with Cordova apps, the client is always fully refreshed 38 | once a change is detected. 39 | 40 | ### `usesCleartextTraffic` 41 | Starting with Android 9 (API level 28), [cleartext support is disabled](https://developer.android.com/training/articles/security-config) by default. 42 | During development `autoupdate` uses cleartext to publish new client versions. 43 | If your app targets Android 9 or greater, it will be necessary to create a 44 | `mobile-config.js` file enabling the use of cleartext in order to have HCP working: 45 | 46 | ```js 47 | App.appendToConfig(` 51 | 52 | 53 | `); 54 | ``` 55 | 56 | ### `--mobile-server` 57 | Additionally, for the HCP functionality to work it is also mandatory to provide 58 | the address for the application server with `--mobile-server` option. If you're 59 | testing your app on an emulator you should run it with `meteor run android --mobile-server 10.0.2.2:3000`. 60 | If you're running it on a real device, the application server and the device 61 | should be on the same network, and you should run your app with `meteor run android --mobile-server XXX.XXX.XXX.XXX` 62 | where *XXX.XXX.XXX.XXX* is your local development address, _e.g. 192.168.1.4_. 63 | 64 | > To have a better understanding of how HCP works for mobile apps already 65 | > published to production refer to [Hot code push on mobile](https://guide.meteor.com/cordova.html#hot-code-push) 66 | -------------------------------------------------------------------------------- /source/packages/browser-policy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: browser-policy 3 | description: Documentation of Meteor's `browser-policy` package. 4 | --- 5 | 6 | The `browser-policy` family of packages, part of 7 | [Webapp](https://github.com/meteor/meteor/tree/master/packages/webapp), lets you 8 | set security-related policies that will be enforced by newer browsers. These 9 | policies help you prevent and mitigate common attacks like cross-site scripting 10 | and clickjacking. 11 | 12 | ## Details 13 | 14 | When you add `browser-policy` to your app, you get default configurations for 15 | the HTTP headers X-Frame-Options and Content-Security-Policy. X-Frame-Options 16 | tells the browser which websites are allowed to frame your app. You should only 17 | let trusted websites frame your app, because malicious sites could harm your 18 | users with [clickjacking attacks](https://www.owasp.org/index.php/Clickjacking). 19 | [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Security/CSP/Introducing_Content_Security_Policy) 20 | tells the browser where your app can load content from, which encourages safe 21 | practices and mitigates the damage of a cross-site-scripting attack. 22 | `browser-policy` also provides functions for you to configure these policies if 23 | the defaults are not suitable. 24 | 25 | If you only want to use Content-Security-Policy or X-Frame-Options but not both, 26 | you can add the individual packages `browser-policy-content` or 27 | `browser-policy-framing` instead of `browser-policy`. 28 | 29 | For most apps, we recommend that you take the following steps: 30 | 31 | * Add `browser-policy` to your app to enable a starter policy. With this starter 32 | policy, your app's client code will be able to load content (images, scripts, 33 | fonts, etc.) only from its own origin, except that XMLHttpRequests and WebSocket 34 | connections can go to any origin. Further, your app's client code will not be 35 | able to use functions such as `eval()` that convert strings to code. Users' 36 | browsers will only let your app be framed by web pages on the same origin as 37 | your app. 38 | * You can use the functions described below to customize the policies. If your 39 | app does not need any inline Javascript such as inline ` 79 | 80 | 81 | ``` 82 | 83 | Then using the connectHandlers method described above serve up your static HTML on app-root/ page load as shown below. 84 | 85 | ``` 86 | /* global WebApp Assets */ 87 | import crypto from 'crypto' 88 | import connectRoute from 'connect-route' 89 | 90 | WebApp.connectHandlers.use(connectRoute(function (router) { 91 | router.get('/', function (req, res, next) { 92 | const buf = Assets.getText('index.html') 93 | 94 | if (buf.length > 0) { 95 | const eTag = crypto.createHash('md5').update(buf).digest('hex') 96 | 97 | if (req.headers['if-none-match'] === eTag) { 98 | res.writeHead(304, 'Not Modified') 99 | return res.end() 100 | } 101 | 102 | res.writeHead(200, { 103 | ETag: eTag, 104 | 'Content-Type': 'text/html' 105 | }) 106 | 107 | return res.end(buf); 108 | } 109 | 110 | return res.end('Index page not found!') 111 | }) 112 | })) 113 | ``` 114 | 115 | There are a couple things to think about with this approach. 116 | 117 | We're reading the contents of index.html using the [Assets](https://docs.meteor.com/api/assets.html) module that makes it really easy to read files out of the _private_ root folder. 118 | 119 | We're using the [connect-route](https://www.npmjs.com/package/connect-route) NPM package to simplify WebApp route processing. But you can use any package you want to understand what is being requested. 120 | 121 | And finally, if you decide to use this technique you'll want to make sure you understand how conflicting client side routing will affect user experience. 122 | -------------------------------------------------------------------------------- /source/windows.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Windows 3 | description: Troubleshooting Meteor on Windows 4 | --- 5 | 6 |

Can't start Mongo server

7 | 8 | If your embed MongoDB is not starting when you run `meteor` and you see messages like these: 9 | 10 | ```shell script 11 | C:\Users\user\app> meteor 12 | => Started proxy. 13 | Unexpected mongo exit code 3221225781. Restarting. 14 | Unexpected mongo exit code 3221225781. Restarting. 15 | Unexpected mongo exit code 3221225781. Restarting. 16 | Can't start Mongo server. 17 | ``` 18 | 19 | You [probably](https://github.com/meteor/meteor/issues/10036#issuecomment-416485306) need to install `Visual C++ Redistributable for Visual Studio 2015`. 20 | 21 | Download it [here](https://www.microsoft.com/en-us/download/confirmation.aspx?id=48145) and install. 22 | 23 | After installing `vc_redist.x64` you should be able to run Meteor and MongoDB server without problems. 24 | --------------------------------------------------------------------------------