mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge pull request #12926 from meteor/docs/meteor-3-docs
[ Meteor 3 ] - API Docs
This commit is contained in:
@@ -419,14 +419,14 @@ export class AccountsCommon {
|
||||
|
||||
/**
|
||||
* @summary Get the current user id, or `null` if no user is logged in. A reactive data source.
|
||||
* @locus Anywhere but publish functions
|
||||
* @locus Anywhere
|
||||
* @importFromPackage meteor
|
||||
*/
|
||||
Meteor.userId = () => Accounts.userId();
|
||||
|
||||
/**
|
||||
* @summary Get the current user record, or `null` if no user is logged in. A reactive data source.
|
||||
* @locus Anywhere but publish functions
|
||||
* @locus Anywhere
|
||||
* @importFromPackage meteor
|
||||
* @param {Object} [options]
|
||||
* @param {MongoFieldSpecifier} options.fields Dictionary of fields to return or exclude.
|
||||
@@ -435,7 +435,7 @@ Meteor.user = options => Accounts.user(options);
|
||||
|
||||
/**
|
||||
* @summary Get the current user record, or `null` if no user is logged in. A reactive data source.
|
||||
* @locus Anywhere but publish functions
|
||||
* @locus Anywhere
|
||||
* @importFromPackage meteor
|
||||
* @param {Object} [options]
|
||||
* @param {MongoFieldSpecifier} options.fields Dictionary of fields to return or exclude.
|
||||
|
||||
@@ -108,7 +108,7 @@ Accounts._checkPasswordAsync = checkPasswordAsync;
|
||||
|
||||
|
||||
/**
|
||||
* @summary Finds the user with the specified username.
|
||||
* @summary Finds the user asynchronously with the specified username.
|
||||
* First tries to match username case sensitively; if that fails, it
|
||||
* tries case insensitively; but if more than one user matches the case
|
||||
* insensitive search, it returns null.
|
||||
@@ -116,7 +116,7 @@ Accounts._checkPasswordAsync = checkPasswordAsync;
|
||||
* @param {String} username The username to look for
|
||||
* @param {Object} [options]
|
||||
* @param {MongoFieldSpecifier} options.fields Dictionary of fields to return or exclude.
|
||||
* @returns {Object} A user if found, else null
|
||||
* @returns {Promise<Object>} A user if found, else null
|
||||
* @importFromPackage accounts-base
|
||||
*/
|
||||
Accounts.findUserByUsername =
|
||||
@@ -124,7 +124,7 @@ Accounts.findUserByUsername =
|
||||
await Accounts._findUserByQuery({ username }, options);
|
||||
|
||||
/**
|
||||
* @summary Finds the user with the specified email.
|
||||
* @summary Finds the user asynchronously with the specified email.
|
||||
* First tries to match email case sensitively; if that fails, it
|
||||
* tries case insensitively; but if more than one user matches the case
|
||||
* insensitive search, it returns null.
|
||||
@@ -132,7 +132,7 @@ Accounts.findUserByUsername =
|
||||
* @param {String} email The email address to look for
|
||||
* @param {Object} [options]
|
||||
* @param {MongoFieldSpecifier} options.fields Dictionary of fields to return or exclude.
|
||||
* @returns {Object} A user if found, else null
|
||||
* @returns {Promise<Object>} A user if found, else null
|
||||
* @importFromPackage accounts-base
|
||||
*/
|
||||
Accounts.findUserByEmail =
|
||||
@@ -219,7 +219,7 @@ Accounts.registerLoginHandler("password", async options => {
|
||||
///
|
||||
|
||||
/**
|
||||
* @summary Change a user's username. Use this instead of updating the
|
||||
* @summary Change a user's username asynchronously. Use this instead of updating the
|
||||
* database directly. The operation will fail if there is an existing user
|
||||
* with a username only differing in case.
|
||||
* @locus Server
|
||||
@@ -378,13 +378,13 @@ Meteor.methods({forgotPassword: async options => {
|
||||
}});
|
||||
|
||||
/**
|
||||
* @summary Generates a reset token and saves it into the database.
|
||||
* @summary Asynchronously generates a reset token and saves it into the database.
|
||||
* @locus Server
|
||||
* @param {String} userId The id of the user to generate the reset token for.
|
||||
* @param {String} email Which address of the user to generate the reset token for. This address must be in the user's `emails` list. If `null`, defaults to the first email in the list.
|
||||
* @param {String} reason `resetPassword` or `enrollAccount`.
|
||||
* @param {Object} [extraTokenData] Optional additional data to be added into the token record.
|
||||
* @returns {Object} Object with {email, user, token} values.
|
||||
* @returns {Promise<Object>} Promise of an object with {email, user, token} values.
|
||||
* @importFromPackage accounts-base
|
||||
*/
|
||||
Accounts.generateResetToken =
|
||||
@@ -452,12 +452,12 @@ Accounts.generateResetToken =
|
||||
};
|
||||
|
||||
/**
|
||||
* @summary Generates an e-mail verification token and saves it into the database.
|
||||
* @summary Generates asynchronously an e-mail verification token and saves it into the database.
|
||||
* @locus Server
|
||||
* @param {String} userId The id of the user to generate the e-mail verification token for.
|
||||
* @param {String} email Which address of the user to generate the e-mail verification token for. This address must be in the user's `emails` list. If `null`, defaults to the first unverified email in the list.
|
||||
* @param {Object} [extraTokenData] Optional additional data to be added into the token record.
|
||||
* @returns {Object} Object with {email, user, token} values.
|
||||
* @returns {Promise<Object>} Promise of an object with {email, user, token} values.
|
||||
* @importFromPackage accounts-base
|
||||
*/
|
||||
Accounts.generateVerificationToken =
|
||||
@@ -517,13 +517,13 @@ Accounts.generateVerificationToken =
|
||||
// to set a new password, without the old password.
|
||||
|
||||
/**
|
||||
* @summary Send an email with a link the user can use to reset their password.
|
||||
* @summary Send an email asynchronously with a link the user can use to reset their password.
|
||||
* @locus Server
|
||||
* @param {String} userId The id of the user to send email to.
|
||||
* @param {String} [email] Optional. Which address of the user's to send the email to. This address must be in the user's `emails` list. Defaults to the first email in the list.
|
||||
* @param {Object} [extraTokenData] Optional additional data to be added into the token record.
|
||||
* @param {Object} [extraParams] Optional additional params to be added to the reset url.
|
||||
* @returns {Object} Object with {email, user, token, url, options} values.
|
||||
* @returns {Promise<Object>} Promise of an object with {email, user, token, url, options} values.
|
||||
* @importFromPackage accounts-base
|
||||
*/
|
||||
Accounts.sendResetPasswordEmail =
|
||||
@@ -549,13 +549,13 @@ Accounts.sendResetPasswordEmail =
|
||||
// want to use enrollment emails.
|
||||
|
||||
/**
|
||||
* @summary Send an email with a link the user can use to set their initial password.
|
||||
* @summary Send an email asynchronously with a link the user can use to set their initial password.
|
||||
* @locus Server
|
||||
* @param {String} userId The id of the user to send email to.
|
||||
* @param {String} [email] Optional. Which address of the user's to send the email to. This address must be in the user's `emails` list. Defaults to the first email in the list.
|
||||
* @param {Object} [extraTokenData] Optional additional data to be added into the token record.
|
||||
* @param {Object} [extraParams] Optional additional params to be added to the enrollment url.
|
||||
* @returns {Object} Object with {email, user, token, url, options} values.
|
||||
* @returns {Promise<Object>} Promise of an object {email, user, token, url, options} values.
|
||||
* @importFromPackage accounts-base
|
||||
*/
|
||||
Accounts.sendEnrollmentEmail =
|
||||
@@ -728,14 +728,13 @@ Meteor.methods(
|
||||
// address as verified
|
||||
|
||||
/**
|
||||
* @summary Send an email with a link the user can use verify their email address.
|
||||
* @summary Send an email asynchronously with a link the user can use verify their email address.
|
||||
* @locus Server
|
||||
* @param {String} userId The id of the user to send email to.
|
||||
* @param {String} [email] Optional. Which address of the user's to send the email to. This address must be in the user's `emails` list. Defaults to the first unverified email in the list.
|
||||
* @param {Object} [extraTokenData] Optional additional data to be added into the token record.
|
||||
* @param {Object} [extraParams] Optional additional params to be added to the verification url.
|
||||
*
|
||||
* @returns {Object} Object with {email, user, token, url, options} values.
|
||||
* @returns {Promise<Object>} Promise of an object with {email, user, token, url, options} values.
|
||||
* @importFromPackage accounts-base
|
||||
*/
|
||||
Accounts.sendVerificationEmail =
|
||||
@@ -831,7 +830,7 @@ Meteor.methods(
|
||||
});
|
||||
|
||||
/**
|
||||
* @summary Add an email address for a user. Use this instead of directly
|
||||
* @summary Add an email asynchronously address for a user. Use this instead of directly
|
||||
* updating the database. The operation will fail if there is a different user
|
||||
* with an email only differing in case. If the specified user has an existing
|
||||
* email only differing in case however, we replace it.
|
||||
@@ -931,7 +930,7 @@ Accounts.addEmail =
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Remove an email address for a user. Use this instead of updating
|
||||
* @summary Remove an email address asynchronously for a user. Use this instead of updating
|
||||
* the database directly.
|
||||
* @locus Server
|
||||
* @param {String} userId The ID of the user to update.
|
||||
@@ -1011,7 +1010,7 @@ Meteor.methods(
|
||||
});
|
||||
|
||||
/**
|
||||
* @summary Creates an user and sends an email if `options.email` is informed.
|
||||
* @summary Creates an user asynchronously and sends an email if `options.email` is informed.
|
||||
* Then if the `sendVerificationEmail` option from the `Accounts` package is
|
||||
* enabled, you'll send a verification email if `options.password` is informed,
|
||||
* otherwise you'll send an enrollment email.
|
||||
|
||||
@@ -384,11 +384,11 @@ EJSON.fromJSONValue = item => {
|
||||
* @locus Anywhere
|
||||
* @param {EJSON} val A value to stringify.
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean | Integer | String} options.indent Indents objects and
|
||||
* @param {Boolean | Integer | String} [options.indent] Indents objects and
|
||||
* arrays for easy readability. When `true`, indents by 2 spaces; when an
|
||||
* integer, indents by that number of spaces; and when a string, uses the
|
||||
* string as the indentation pattern.
|
||||
* @param {Boolean} options.canonical When `true`, stringifies keys in an
|
||||
* @param {Boolean} [options.canonical] When `true`, stringifies keys in an
|
||||
* object in sorted order.
|
||||
*/
|
||||
EJSON.stringify = handleError((item, options) => {
|
||||
|
||||
@@ -203,7 +203,7 @@ Email.customTransport = undefined;
|
||||
* @locus Server
|
||||
* @return {Promise}
|
||||
* @param {Object} options
|
||||
* @param {String} [options.from] "From:" address (required)
|
||||
* @param {String} options.from "From:" address (required)
|
||||
* @param {String|String[]} options.to,cc,bcc,replyTo
|
||||
* "To:", "Cc:", "Bcc:", and "Reply-To:" addresses
|
||||
* @param {String} [options.inReplyTo] Message-ID this message is replying to
|
||||
@@ -273,7 +273,7 @@ Email.sendAsync = async function (options) {
|
||||
* @locus Server
|
||||
* @return {Promise}
|
||||
* @param {Object} options
|
||||
* @param {String} [options.from] "From:" address (required)
|
||||
* @param {String} options.from "From:" address (required)
|
||||
* @param {String|String[]} options.to,cc,bcc,replyTo
|
||||
* "To:", "Cc:", "Bcc:", and "Reply-To:" addresses
|
||||
* @param {String} [options.inReplyTo] Message-ID this message is replying to
|
||||
|
||||
@@ -44,7 +44,7 @@ class EnvironmentVariableAsync {
|
||||
* @method withValue
|
||||
* @param {any} value The value to set for the duration of the function call
|
||||
* @param {Function} func The function to call with the new value of the
|
||||
* @param {Object} [options] Optional additional options
|
||||
* @param {Object} [options] Optional additional properties for adding in [asl](https://nodejs.org/api/async_context.html#class-asynclocalstorage)
|
||||
* @returns {Promise<any>} The return value of the function
|
||||
*/
|
||||
withValue(value, func, options = {}) {
|
||||
|
||||
@@ -42,23 +42,7 @@ Meteor.makeErrorType = function (name, constructor) {
|
||||
* @param {String} error A string code uniquely identifying this kind of error.
|
||||
* This string should be used by callers of the method to determine the
|
||||
* appropriate action to take, instead of attempting to parse the reason
|
||||
* or details fields. For example:
|
||||
*
|
||||
* ```
|
||||
* // on the server, pick a code unique to this error
|
||||
* // the reason field should be a useful debug message
|
||||
* throw new Meteor.Error("logged-out",
|
||||
* "The user must be logged in to post a comment.");
|
||||
*
|
||||
* // on the client
|
||||
* Meteor.call("methodName", function (error) {
|
||||
* // identify the error
|
||||
* if (error && error.error === "logged-out") {
|
||||
* // show a nice error message
|
||||
* Session.set("errorMessage", "Please log in to post a comment.");
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
* or details fields.
|
||||
*
|
||||
* For legacy reasons, some built-in Meteor functions such as `check` throw
|
||||
* errors with a number in this field.
|
||||
|
||||
@@ -20,7 +20,7 @@ Mongo = {};
|
||||
* @class
|
||||
* @param {String} name The name of the collection. If null, creates an unmanaged (unsynchronized) local collection.
|
||||
* @param {Object} [options]
|
||||
* @param {Object} options.connection The server connection that will manage this collection. Uses the default connection if not specified. Pass the return value of calling [`DDP.connect`](#ddp_connect) to specify a different server. Pass `null` to specify no connection. Unmanaged (`name` is null) collections cannot specify a connection.
|
||||
* @param {Object} options.connection The server connection that will manage this collection. Uses the default connection if not specified. Pass the return value of calling [`DDP.connect`](#DDP-connect) to specify a different server. Pass `null` to specify no connection. Unmanaged (`name` is null) collections cannot specify a connection.
|
||||
* @param {String} options.idGeneration The method of generating the `_id` fields of new documents in this collection. Possible values:
|
||||
|
||||
- **`'STRING'`**: random strings
|
||||
|
||||
@@ -92,12 +92,10 @@
|
||||
"version": "3.523.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.523.0.tgz",
|
||||
"integrity": "sha512-ggAkL8szaJkqD8oOsS68URJ9XMDbLA/INO/NPZJqv9BhmftecJvfy43uUVWGNs6n4YXNzfF0Y+zQ3DT0fZkv9g=="
|
||||
},
|
||||
"@aws-sdk/core": {
|
||||
"version": "3.523.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.523.0.tgz",
|
||||
"integrity": "sha512-JHa3ngEWkTzZ2YTn6EavcADC8gv6zZU4U9WBAleClh6ioXH0kGMBawZje3y0F0mKyLTfLhFqFUlCV5sngI/Qcw=="
|
||||
},
|
||||
"@aws-sdk/credential-provider-cognito-identity": {
|
||||
"version": "3.523.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.523.0.tgz",
|
||||
@@ -167,7 +165,6 @@
|
||||
"version": "3.523.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.523.0.tgz",
|
||||
"integrity": "sha512-IypIAecBc8b4jM0uVBEj90NYaIsc0vuLdSFyH4LPO7is4rQUet4CkkD+S036NvDdcdxBsQ4hJZBmWrqiizMHhQ=="
|
||||
},
|
||||
"@aws-sdk/token-providers": {
|
||||
"version": "3.523.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.523.0.tgz",
|
||||
@@ -222,7 +219,6 @@
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-1.3.5.tgz",
|
||||
"integrity": "sha512-Rrc+e2Jj6Gu7Xbn0jvrzZlSiP2CZocIOfZ9aNUA82+1sa6GBnxqL9+iZ9EKHeD9aqD1nU8EK4+oN2EiFpSv7Yw=="
|
||||
},
|
||||
"@smithy/credential-provider-imds": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.2.4.tgz",
|
||||
@@ -242,7 +238,6 @@
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.1.3.tgz",
|
||||
"integrity": "sha512-FsAPCUj7VNJIdHbSxMd5uiZiF20G2zdSDgrgrDrHqIs/VMxK85Vqk5kMVNNDMCZmMezp6UKnac0B4nAyx7HJ9g=="
|
||||
},
|
||||
"@smithy/invalid-dependency": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.1.3.tgz",
|
||||
@@ -317,7 +312,6 @@
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.3.4.tgz",
|
||||
"integrity": "sha512-CiZmPg9GeDKbKmJGEFvJBsJcFnh0AQRzOtQAzj1XEa8N/0/uSN/v1LYzgO7ry8hhO8+9KB7+DhSW0weqBra4Aw=="
|
||||
},
|
||||
"@smithy/signature-v4": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.1.3.tgz",
|
||||
|
||||
6
v3-docs/docs/.gitignore
vendored
Normal file
6
v3-docs/docs/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/node_modules
|
||||
/.vitepress/cache
|
||||
/.vitepress/dist
|
||||
|
||||
/data/data.js
|
||||
/data/names.json
|
||||
292
v3-docs/docs/.vitepress/config.mts
Normal file
292
v3-docs/docs/.vitepress/config.mts
Normal file
@@ -0,0 +1,292 @@
|
||||
import { defineConfig } from "vitepress";
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title: "Meteor API Docs",
|
||||
description: "Meteor.js API docs",
|
||||
head: [["link", { rel: "icon", href: "/logo.png" }]],
|
||||
lastUpdated: true,
|
||||
themeConfig: {
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
nav: [
|
||||
{ text: "Galaxy", link: "https://www.meteor.com/cloud" },
|
||||
{
|
||||
text: "Tutorials",
|
||||
link: "https://www.meteor.com/developers/tutorials",
|
||||
},
|
||||
{ text: "Guide", link: "https://guide.meteor.com/" },
|
||||
{ text: "API Docs", link: "/about/what-is" },
|
||||
{ text: "Forums", link: "https://forums.meteor.com/" },
|
||||
],
|
||||
sidebar: [
|
||||
{
|
||||
text: "About",
|
||||
items: [
|
||||
{
|
||||
text: "What is Meteor?",
|
||||
link: "/about/what-is#what-is-meteor",
|
||||
},
|
||||
{
|
||||
text: "Meteor resources",
|
||||
link: "/about/what-is#learning-more",
|
||||
},
|
||||
{
|
||||
text: "Roadmap",
|
||||
link: "/about/roadmap",
|
||||
},
|
||||
],
|
||||
collapsed: true,
|
||||
},
|
||||
{
|
||||
text: "Quick Start",
|
||||
items: [
|
||||
{
|
||||
text: "Install Meteor",
|
||||
link: "/about/install",
|
||||
},
|
||||
// TODO: Your first app meteor app
|
||||
],
|
||||
collapsed: true,
|
||||
},
|
||||
{
|
||||
text: "API",
|
||||
link: "/api/index",
|
||||
items: [
|
||||
{
|
||||
text: "Accounts",
|
||||
link: "/api/accounts",
|
||||
items: [
|
||||
{ text: "Accounts-Base", link: "/api/accounts#accounts-base" },
|
||||
{ text: "Multi-server", link: "/api/accounts#multi-server" },
|
||||
{ text: "Passwords", link: "/api/accounts#passwords" },
|
||||
],
|
||||
collapsed: true,
|
||||
},
|
||||
{
|
||||
text: "Meteor",
|
||||
link: "/api/meteor",
|
||||
items: [
|
||||
{ text: "Core", link: "/api/meteor#core" },
|
||||
{ text: "Methods", link: "/api/meteor#methods" },
|
||||
{ text: "Publish and Subscribe", link: "/api/meteor#pubsub" },
|
||||
{ text: "Server connections", link: "/api/meteor#connections" },
|
||||
{ text: "Timers", link: "/api/meteor#timers" },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Collections",
|
||||
link: "/api/collections",
|
||||
},
|
||||
{
|
||||
text: "DDPRateLimiter",
|
||||
link: "/api/DDPRateLimiter",
|
||||
},
|
||||
{
|
||||
text: "Check",
|
||||
link: "/api/check",
|
||||
},
|
||||
{
|
||||
text: "Session",
|
||||
link: "/api/session",
|
||||
},
|
||||
{
|
||||
text: "Blaze",
|
||||
link: "/api/blaze",
|
||||
},
|
||||
{
|
||||
text: "Templates",
|
||||
link: "/api/templates",
|
||||
},
|
||||
{
|
||||
text: "Email",
|
||||
link: "/api/email",
|
||||
},
|
||||
{
|
||||
text: "Tracker",
|
||||
link: "/api/Tracker",
|
||||
},
|
||||
{
|
||||
text: "Reactive Var",
|
||||
link: "/api/ReactiveVar",
|
||||
},
|
||||
{
|
||||
text: "Reactive Dict",
|
||||
link: "/api/ReactiveDict",
|
||||
},
|
||||
{
|
||||
text: "EJSON",
|
||||
link: "/api/EJSON",
|
||||
},
|
||||
{
|
||||
text: "Assets",
|
||||
link: "/api/assets",
|
||||
},
|
||||
{
|
||||
text: "Mobile Configuration",
|
||||
link: "/api/app",
|
||||
},
|
||||
{
|
||||
text: "Package.js",
|
||||
link: "/api/package",
|
||||
},
|
||||
],
|
||||
collapsed: false,
|
||||
},
|
||||
{
|
||||
text: "Packages",
|
||||
items: [
|
||||
{
|
||||
text: "accounts-ui",
|
||||
link: "/packages/accounts-ui",
|
||||
},
|
||||
{
|
||||
text: "accounts-passwordless",
|
||||
link: "/packages/accounts-passwordless",
|
||||
},
|
||||
{
|
||||
text: "accounts-2fa",
|
||||
link: "/packages/accounts-2fa",
|
||||
},
|
||||
{
|
||||
text: "appcache",
|
||||
link: "/packages/appcache",
|
||||
},
|
||||
{
|
||||
text: "audit-arguments-checks",
|
||||
link: "/packages/audit-argument-checks",
|
||||
},
|
||||
{
|
||||
text: "autoupdate",
|
||||
link: "/packages/autoupdate",
|
||||
},
|
||||
{
|
||||
text: "browser-policy",
|
||||
link: "/packages/browser-policy",
|
||||
},
|
||||
{
|
||||
text: "bundler-visualizer",
|
||||
link: "/packages/bundle-visualizer",
|
||||
},
|
||||
{
|
||||
text: "coffeescript",
|
||||
link: "/packages/coffeescript",
|
||||
},
|
||||
{
|
||||
text: "ecmascript",
|
||||
link: "/packages/ecmascript",
|
||||
},
|
||||
{
|
||||
text: "fetch",
|
||||
link: "/packages/fetch",
|
||||
},
|
||||
{
|
||||
text: "hot-module-replacement",
|
||||
link: "/packages/hot-module-replacement",
|
||||
},
|
||||
{
|
||||
text: "less",
|
||||
link: "/packages/less",
|
||||
},
|
||||
{
|
||||
text: "logging",
|
||||
link: "/packages/logging",
|
||||
},
|
||||
{
|
||||
text: "markdown",
|
||||
link: "/packages/markdown",
|
||||
},
|
||||
{
|
||||
text: "modules",
|
||||
link: "/packages/modules",
|
||||
},
|
||||
{
|
||||
text: "oauth-encryption",
|
||||
link: "/packages/oauth-encryption",
|
||||
},
|
||||
{
|
||||
text: "random",
|
||||
link: "/packages/random",
|
||||
},
|
||||
{
|
||||
text: "server-render",
|
||||
link: "/packages/server-render",
|
||||
},
|
||||
{
|
||||
text: "standard-minifier-css",
|
||||
link: "/packages/standard-minifier-css",
|
||||
},
|
||||
{
|
||||
text: "underscore",
|
||||
link: "/packages/underscore",
|
||||
},
|
||||
{
|
||||
text: "url",
|
||||
link: "/packages/url",
|
||||
},
|
||||
{
|
||||
text: "webapp",
|
||||
link: "/packages/webapp",
|
||||
},
|
||||
{
|
||||
link: "packages/packages-listing",
|
||||
text: "Maintained Packages",
|
||||
},
|
||||
],
|
||||
collapsed: true,
|
||||
},
|
||||
{
|
||||
text: "Troubleshooting",
|
||||
items: [
|
||||
{
|
||||
text: "Expired Certificates",
|
||||
link: "/troubleshooting/expired-certificate",
|
||||
},
|
||||
{ text: "Windows", link: "/troubleshooting/windows" },
|
||||
{
|
||||
text: "Known issues in 2.13",
|
||||
link: "/troubleshooting/known-issues",
|
||||
},
|
||||
],
|
||||
collapsed: true,
|
||||
},
|
||||
{
|
||||
text: "Command Line",
|
||||
items: [
|
||||
{ link: "cli/index", text: "CLI" },
|
||||
{ link: "cli/using-core-types", text: "Using Core Types" },
|
||||
{ link: "cli/environment-variables", text: "Environment Variables" },
|
||||
],
|
||||
collapsed: true,
|
||||
},
|
||||
{
|
||||
text: "Changelog",
|
||||
items: [
|
||||
// TODO: Open issue in Vitepress about this
|
||||
{ link: "/history", text: "Current" },
|
||||
{
|
||||
link: "https://docs.meteor.com/changelog#v2020210120",
|
||||
text: "Pre-2.0",
|
||||
},
|
||||
],
|
||||
collapsed: true,
|
||||
},
|
||||
],
|
||||
|
||||
socialLinks: [{ icon: "github", link: "https://github.com/meteor/meteor" }],
|
||||
logo: { dark: "/meteor-logo.png", light: "/meteor-blue.png" },
|
||||
|
||||
search: {
|
||||
provider: "local",
|
||||
},
|
||||
footer: {
|
||||
message:
|
||||
'Released under the <a href="https://github.com/meteor/meteor?tab=License-1-ov-file#readme">MIT License</a>.',
|
||||
copyright:
|
||||
'Copyright (c) 2011 - present <a href="https://www.meteor.com/">Meteor Software</a>.',
|
||||
},
|
||||
editLink: {
|
||||
pattern: "https://github.com/meteor/meteor/edit/main/v3-docs/docs/:path",
|
||||
text: "Edit this page on GitHub",
|
||||
},
|
||||
},
|
||||
});
|
||||
80
v3-docs/docs/.vitepress/theme/Layout.vue
Normal file
80
v3-docs/docs/.vitepress/theme/Layout.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<script setup lang="ts">
|
||||
import { useData, useRouter } from 'vitepress'
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import { nextTick, provide } from 'vue'
|
||||
import { redirect } from './redirects/script';
|
||||
const { isDark } = useData()
|
||||
const router = useRouter()
|
||||
|
||||
const isClient = typeof window !== 'undefined';
|
||||
|
||||
if (isClient) {
|
||||
const { path, shouldRedirect } = redirect(window.location.href)
|
||||
|
||||
if (shouldRedirect) router.go(path)
|
||||
}
|
||||
|
||||
const enableTransitions = () =>
|
||||
isClient &&
|
||||
'startViewTransition' in document &&
|
||||
window.matchMedia('(prefers-reduced-motion: no-preference)').matches
|
||||
|
||||
provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
|
||||
if (!enableTransitions()) {
|
||||
isDark.value = !isDark.value
|
||||
return
|
||||
}
|
||||
|
||||
const clipPath = [
|
||||
`circle(0px at ${x}px ${y}px)`,
|
||||
`circle(${Math.hypot(
|
||||
Math.max(x, innerWidth - x),
|
||||
Math.max(y, innerHeight - y)
|
||||
)}px at ${x}px ${y}px)`
|
||||
]
|
||||
|
||||
await document.startViewTransition(async () => {
|
||||
isDark.value = !isDark.value
|
||||
await nextTick()
|
||||
}).ready
|
||||
|
||||
document.documentElement.animate(
|
||||
{ clipPath: isDark.value ? clipPath.reverse() : clipPath },
|
||||
{
|
||||
duration: 300,
|
||||
easing: 'ease-in',
|
||||
pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)`
|
||||
}
|
||||
)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DefaultTheme.Layout />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
::view-transition-old(root),
|
||||
::view-transition-new(root) {
|
||||
animation: none;
|
||||
mix-blend-mode: normal;
|
||||
}
|
||||
|
||||
::view-transition-old(root),
|
||||
.dark::view-transition-new(root) {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
::view-transition-new(root),
|
||||
.dark::view-transition-old(root) {
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.VPSwitchAppearance {
|
||||
width: 22px !important;
|
||||
}
|
||||
|
||||
.VPSwitchAppearance .check {
|
||||
transform: none !important;
|
||||
}
|
||||
</style>
|
||||
17
v3-docs/docs/.vitepress/theme/index.ts
Normal file
17
v3-docs/docs/.vitepress/theme/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
// .vitepress/theme/index.ts
|
||||
import type { Theme } from "vitepress";
|
||||
import DefaultTheme from "vitepress/theme";
|
||||
import ApiBox from "../../components/ApiBox.vue";
|
||||
import ApiMap from "../../components/ApiMap.vue";
|
||||
import Layout from "./Layout.vue";
|
||||
import "./theme.css";
|
||||
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
enhanceApp({ app }) {
|
||||
// register your custom global components
|
||||
app.component("ApiBox", ApiBox);
|
||||
app.component("ApiMap", ApiMap);
|
||||
},
|
||||
Layout,
|
||||
} satisfies Theme;
|
||||
4
v3-docs/docs/.vitepress/theme/redirects/redirects.json
Normal file
4
v3-docs/docs/.vitepress/theme/redirects/redirects.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"meteor_call": "meteor-call",
|
||||
"ddp_connect": "DDP-connect"
|
||||
}
|
||||
18
v3-docs/docs/.vitepress/theme/redirects/script.js
Normal file
18
v3-docs/docs/.vitepress/theme/redirects/script.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// import json from './redirects.json';
|
||||
/**
|
||||
*
|
||||
* @param {string} path
|
||||
*/
|
||||
export const redirect = (path) => {
|
||||
let shouldRedirect = false;
|
||||
console.log(path)
|
||||
if (path.includes("_")) {
|
||||
shouldRedirect = true;
|
||||
path = path.replace("_", "-");
|
||||
}
|
||||
|
||||
return {
|
||||
path,
|
||||
shouldRedirect
|
||||
};
|
||||
};
|
||||
557
v3-docs/docs/.vitepress/theme/theme.css
Normal file
557
v3-docs/docs/.vitepress/theme/theme.css
Normal file
@@ -0,0 +1,557 @@
|
||||
/**
|
||||
* Colors: Solid
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-c-white: #ffffff;
|
||||
--vp-c-black: #000000;
|
||||
|
||||
--vp-c-neutral: var(--vp-c-black);
|
||||
--vp-c-neutral-inverse: var(--vp-c-white);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--vp-c-neutral: var(--vp-c-white);
|
||||
--vp-c-neutral-inverse: var(--vp-c-black);
|
||||
}
|
||||
|
||||
/**
|
||||
* Colors: Palette
|
||||
*
|
||||
* The primitive colors used for accent colors. These colors are referenced
|
||||
* by functional colors such as "Text", "Background", or "Brand".
|
||||
*
|
||||
* Each colors have exact same color scale system with 3 levels of solid
|
||||
* colors with different brightness, and 1 soft color.
|
||||
*
|
||||
* - `XXX-1`: The most solid color used mainly for colored text. It must
|
||||
* satisfy the contrast ratio against when used on top of `XXX-soft`.
|
||||
*
|
||||
* - `XXX-2`: The color used mainly for hover state of the button.
|
||||
*
|
||||
* - `XXX-3`: The color for solid background, such as bg color of the button.
|
||||
* It must satisfy the contrast ratio with pure white (#ffffff) text on
|
||||
* top of it.
|
||||
*
|
||||
* - `XXX-soft`: The color used for subtle background such as custom container
|
||||
* or badges. It must satisfy the contrast ratio when putting `XXX-1` colors
|
||||
* on top of it.
|
||||
*
|
||||
* The soft color must be semi transparent alpha channel. This is crucial
|
||||
* because it allows adding multiple "soft" colors on top of each other
|
||||
* to create a accent, such as when having inline code block inside
|
||||
* custom containers.
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-c-gray-1: #dddde3;
|
||||
--vp-c-gray-2: #e4e4e9;
|
||||
--vp-c-gray-3: #ebebef;
|
||||
--vp-c-gray-soft: rgba(142, 150, 170, 0.14);
|
||||
|
||||
--vp-c-indigo-1: #3451b2;
|
||||
--vp-c-indigo-2: #3a5ccc;
|
||||
--vp-c-indigo-3: #5672cd;
|
||||
--vp-c-indigo-soft: rgba(100, 108, 255, 0.14);
|
||||
|
||||
--vp-c-blue-1: #383695;
|
||||
--vp-c-blue-2: #3e3e9e;
|
||||
--vp-c-blue-3: #4c4ca7;
|
||||
--vp-c-blue-soft: rgba(100, 108, 255, 0.14);
|
||||
|
||||
--vp-c-green-1: #18794e;
|
||||
--vp-c-green-2: #299764;
|
||||
--vp-c-green-3: #30a46c;
|
||||
--vp-c-green-soft: rgba(16, 185, 129, 0.14);
|
||||
|
||||
--vp-c-orange-1: #FF6A3E;
|
||||
--vp-c-orange-2: #FF7A4D;
|
||||
--vp-c-orange-3: #FF8A5C;
|
||||
--vp-c-orange-soft: rgba(255, 106, 62, 0.14);
|
||||
|
||||
--vp-c-meteor-red-1: #BF212E;
|
||||
--vp-c-meteor-red-2: #A01A24;
|
||||
--vp-c-meteor-red-3: #7F141D;
|
||||
--vp-c-meteor-red-soft: rgba(191, 33, 46, 0.16);
|
||||
|
||||
--vp-c-yellow-1: #915930;
|
||||
--vp-c-yellow-2: #946300;
|
||||
--vp-c-yellow-3: #9f6a00;
|
||||
--vp-c-yellow-soft: rgba(234, 179, 8, 0.14);
|
||||
|
||||
--vp-c-red-1: #b8272c;
|
||||
--vp-c-red-2: #d5393e;
|
||||
--vp-c-red-3: #e0575b;
|
||||
--vp-c-red-soft: rgba(244, 63, 94, 0.14);
|
||||
|
||||
--vp-c-sponsor: #db2777;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--vp-c-gray-1: #515c67;
|
||||
--vp-c-gray-2: #414853;
|
||||
--vp-c-gray-3: #32363f;
|
||||
--vp-c-gray-soft: rgba(101, 117, 133, 0.16);
|
||||
|
||||
--vp-c-indigo-1: #a8b1ff;
|
||||
--vp-c-indigo-2: #5c73e7;
|
||||
--vp-c-indigo-3: #3e63dd;
|
||||
--vp-c-indigo-soft: rgba(100, 108, 255, 0.16);
|
||||
|
||||
--vp-c-blue-1: #4b6cb7;
|
||||
--vp-c-blue-2: #3e4f8c;
|
||||
--vp-c-blue-3: #36436b;
|
||||
--vp-c-blue-soft: rgba(100, 108, 255, 0.16);
|
||||
|
||||
--vp-c-green-1: #3dd68c;
|
||||
--vp-c-green-2: #30a46c;
|
||||
--vp-c-green-3: #298459;
|
||||
--vp-c-green-soft: rgba(16, 185, 129, 0.16);
|
||||
|
||||
--vc-c-orange-1: #ff7e17;
|
||||
--vc-c-orange-2: #e66c00;
|
||||
--vc-c-orange-3: #b35e00;
|
||||
--vc-c-orange-soft: rgba(255, 126, 23, 0.16);
|
||||
|
||||
--vc-c-meteor-red-1: #BF212E;
|
||||
--vc-c-meteor-red-2: #A01A24;
|
||||
--vc-c-meteor-red-3: #7F141D;
|
||||
--vc-c-meteor-red-soft: rgba(191, 33, 46, 0.16);
|
||||
|
||||
--vp-c-yellow-1: #f9b44e;
|
||||
--vp-c-yellow-2: #da8b17;
|
||||
--vp-c-yellow-3: #a46a0a;
|
||||
--vp-c-yellow-soft: rgba(234, 179, 8, 0.16);
|
||||
|
||||
--vp-c-red-1: #f66f81;
|
||||
--vp-c-red-2: #f14158;
|
||||
--vp-c-red-3: #b62a3c;
|
||||
--vp-c-red-soft: rgba(244, 63, 94, 0.16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Colors: Background
|
||||
*
|
||||
* - `bg`: The bg color used for main screen.
|
||||
*
|
||||
* - `bg-alt`: The alternative bg color used in places such as "sidebar",
|
||||
* or "code block".
|
||||
*
|
||||
* - `bg-elv`: The elevated bg color. This is used at parts where it "floats",
|
||||
* such as "dialog".
|
||||
*
|
||||
* - `bg-soft`: The bg color to slightly distinguish some components from
|
||||
* the page. Used for things like "carbon ads" or "table".
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-c-bg: #ffffff;
|
||||
--vp-c-bg-alt: #f6f6f7;
|
||||
--vp-c-bg-elv: #ffffff;
|
||||
--vp-c-bg-soft: #f6f6f7;
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
||||
--vp-c-bg: #101926;
|
||||
--vp-c-bg-alt: #0a0f1f;
|
||||
--vp-c-bg-elv: #101926;
|
||||
--vp-c-bg-soft: #0a0f1f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Colors: Borders
|
||||
*
|
||||
* - `divider`: This is used for separators. This is used to divide sections
|
||||
* within the same components, such as having separator on "h2" heading.
|
||||
*
|
||||
* - `border`: This is designed for borders on interactive components.
|
||||
* For example this should be used for a button outline.
|
||||
*
|
||||
* - `gutter`: This is used to divide components in the page. For example
|
||||
* the header and the lest of the page.
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-c-border: #c2c2c4;
|
||||
--vp-c-divider: #e2e2e3;
|
||||
--vp-c-gutter: #e2e2e3;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--vp-c-border: #3c3f44;
|
||||
--vp-c-divider: #2e2e32;
|
||||
--vp-c-gutter: #000000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Colors: Text
|
||||
*
|
||||
* - `text-1`: Used for primary text.
|
||||
*
|
||||
* - `text-2`: Used for muted texts, such as "inactive menu" or "info texts".
|
||||
*
|
||||
* - `text-3`: Used for subtle texts, such as "placeholders" or "caret icon".
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-c-text-1: rgba(60, 60, 67);
|
||||
--vp-c-text-2: rgba(60, 60, 67, 0.78);
|
||||
--vp-c-text-3: rgba(60, 60, 67, 0.56);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--vp-c-text-1: rgba(255, 255, 245, 0.86);
|
||||
--vp-c-text-2: rgba(235, 235, 245, 0.6);
|
||||
--vp-c-text-3: rgba(235, 235, 245, 0.38);
|
||||
}
|
||||
|
||||
/**
|
||||
* Colors: Function
|
||||
*
|
||||
* - `default`: The color used purely for subtle indication without any
|
||||
* special meanings attached to it such as bg color for menu hover state.
|
||||
*
|
||||
* - `brand`: Used for primary brand colors, such as link text, button with
|
||||
* brand theme, etc.
|
||||
*
|
||||
* - `tip`: Used to indicate useful information. The default theme uses the
|
||||
* brand color for this by default.
|
||||
*
|
||||
* - `warning`: Used to indicate warning to the users. Used in custom
|
||||
* container, badges, etc.
|
||||
*
|
||||
* - `danger`: Used to show error, or dangerous message to the users. Used
|
||||
* in custom container, badges, etc.
|
||||
*
|
||||
* To understand the scaling system, refer to "Colors: Palette" section.
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-c-default-1: var(--vp-c-gray-1);
|
||||
--vp-c-default-2: var(--vp-c-gray-2);
|
||||
--vp-c-default-3: var(--vp-c-gray-3);
|
||||
--vp-c-default-soft: var(--vp-c-gray-soft);
|
||||
|
||||
--vp-c-brand-1: var(--vp-c-meteor-red-1);
|
||||
--vp-c-brand-2: var(--vp-c-meteor-red-2);
|
||||
--vp-c-brand-3: var(--vp-c-meteor-red-3);
|
||||
--vp-c-brand-soft: var(--vp-c-blue-soft);
|
||||
|
||||
/* DEPRECATED: Use `--vp-c-brand-1` instead. */
|
||||
--vp-c-brand: var(--vp-c-brand-1);
|
||||
|
||||
--vp-c-tip-1: var(--vp-c-brand-1);
|
||||
--vp-c-tip-2: var(--vp-c-brand-2);
|
||||
--vp-c-tip-3: var(--vp-c-brand-3);
|
||||
--vp-c-tip-soft: var(--vp-c-brand-soft);
|
||||
|
||||
--vp-c-warning-1: var(--vp-c-yellow-1);
|
||||
--vp-c-warning-2: var(--vp-c-yellow-2);
|
||||
--vp-c-warning-3: var(--vp-c-yellow-3);
|
||||
--vp-c-warning-soft: var(--vp-c-yellow-soft);
|
||||
|
||||
--vp-c-danger-1: var(--vp-c-red-1);
|
||||
--vp-c-danger-2: var(--vp-c-red-2);
|
||||
--vp-c-danger-3: var(--vp-c-red-3);
|
||||
--vp-c-danger-soft: var(--vp-c-red-soft);
|
||||
}
|
||||
|
||||
/**
|
||||
* Typography
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-font-family-base: 'Chinese Quotes', 'Inter var', 'Inter', ui-sans-serif,
|
||||
system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Helvetica, Arial, 'Noto Sans', sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--vp-font-family-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco,
|
||||
Consolas, 'Liberation Mono', 'Courier New', monospace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shadows
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||
--vp-shadow-2: 0 3px 12px rgba(0, 0, 0, 0.07), 0 1px 4px rgba(0, 0, 0, 0.07);
|
||||
--vp-shadow-3: 0 12px 32px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.08);
|
||||
--vp-shadow-4: 0 14px 44px rgba(0, 0, 0, 0.12), 0 3px 9px rgba(0, 0, 0, 0.12);
|
||||
--vp-shadow-5: 0 18px 56px rgba(0, 0, 0, 0.16), 0 4px 12px rgba(0, 0, 0, 0.16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Z-indexes
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-z-index-footer: 10;
|
||||
--vp-z-index-local-nav: 20;
|
||||
--vp-z-index-nav: 30;
|
||||
--vp-z-index-layout-top: 40;
|
||||
--vp-z-index-backdrop: 50;
|
||||
--vp-z-index-sidebar: 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* Icons
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-icon-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E");
|
||||
--vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/**
|
||||
* Layouts
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-layout-max-width: 1440px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Header Anchor
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-header-anchor-symbol: '#';
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Code
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-code-line-height: 1.7;
|
||||
--vp-code-font-size: 0.875em;
|
||||
--vp-code-color: var(--vp-c-brand-1);
|
||||
--vp-code-link-color: var(--vp-c-brand-1);
|
||||
--vp-code-link-hover-color: var(--vp-c-brand-2);
|
||||
--vp-code-bg: var(--vp-c-default-soft);
|
||||
|
||||
--vp-code-block-color: var(--vp-c-text-2);
|
||||
--vp-code-block-bg: var(--vp-c-bg-alt);
|
||||
--vp-code-block-divider-color: var(--vp-c-gutter);
|
||||
|
||||
--vp-code-lang-color: var(--vp-c-text-3);
|
||||
|
||||
--vp-code-line-highlight-color: var(--vp-c-default-soft);
|
||||
--vp-code-line-number-color: var(--vp-c-text-3);
|
||||
|
||||
--vp-code-line-diff-add-color: var(--vp-c-green-soft);
|
||||
--vp-code-line-diff-add-symbol-color: var(--vp-c-green-1);
|
||||
|
||||
--vp-code-line-diff-remove-color: var(--vp-c-red-soft);
|
||||
--vp-code-line-diff-remove-symbol-color: var(--vp-c-red-1);
|
||||
|
||||
--vp-code-line-warning-color: var(--vp-c-yellow-soft);
|
||||
--vp-code-line-error-color: var(--vp-c-red-soft);
|
||||
|
||||
--vp-code-copy-code-border-color: var(--vp-c-divider);
|
||||
--vp-code-copy-code-bg: var(--vp-c-bg-soft);
|
||||
--vp-code-copy-code-hover-border-color: var(--vp-c-divider);
|
||||
--vp-code-copy-code-hover-bg: var(--vp-c-bg);
|
||||
--vp-code-copy-code-active-text: var(--vp-c-text-2);
|
||||
--vp-code-copy-copied-text-content: 'Copied';
|
||||
|
||||
--vp-code-tab-divider: var(--vp-code-block-divider-color);
|
||||
--vp-code-tab-text-color: var(--vp-c-text-2);
|
||||
--vp-code-tab-bg: var(--vp-code-block-bg);
|
||||
--vp-code-tab-hover-text-color: var(--vp-c-text-1);
|
||||
--vp-code-tab-active-text-color: var(--vp-c-text-1);
|
||||
--vp-code-tab-active-bar-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Button
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-button-brand-border: transparent;
|
||||
--vp-button-brand-text: var(--vp-c-white);
|
||||
--vp-button-brand-bg: var(--vp-c-blue-3);
|
||||
--vp-button-brand-hover-border: transparent;
|
||||
--vp-button-brand-hover-text: var(--vp-c-white);
|
||||
--vp-button-brand-hover-bg: var(--vp-c-blue-2);
|
||||
--vp-button-brand-active-border: transparent;
|
||||
--vp-button-brand-active-text: var(--vp-c-white);
|
||||
--vp-button-brand-active-bg: var(--vp-c-blue-1);
|
||||
|
||||
--vp-button-alt-border: transparent;
|
||||
--vp-button-alt-text: var(--vp-c-text-1);
|
||||
--vp-button-alt-bg: var(--vp-c-default-3);
|
||||
--vp-button-alt-hover-border: transparent;
|
||||
--vp-button-alt-hover-text: var(--vp-c-text-1);
|
||||
--vp-button-alt-hover-bg: var(--vp-c-default-2);
|
||||
--vp-button-alt-active-border: transparent;
|
||||
--vp-button-alt-active-text: var(--vp-c-text-1);
|
||||
--vp-button-alt-active-bg: var(--vp-c-default-1);
|
||||
|
||||
--vp-button-sponsor-border: var(--vp-c-text-2);
|
||||
--vp-button-sponsor-text: var(--vp-c-text-2);
|
||||
--vp-button-sponsor-bg: transparent;
|
||||
--vp-button-sponsor-hover-border: var(--vp-c-sponsor);
|
||||
--vp-button-sponsor-hover-text: var(--vp-c-sponsor);
|
||||
--vp-button-sponsor-hover-bg: transparent;
|
||||
--vp-button-sponsor-active-border: var(--vp-c-sponsor);
|
||||
--vp-button-sponsor-active-text: var(--vp-c-sponsor);
|
||||
--vp-button-sponsor-active-bg: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Custom Block
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-custom-block-font-size: 14px;
|
||||
--vp-custom-block-code-font-size: 13px;
|
||||
|
||||
--vp-custom-block-info-border: transparent;
|
||||
--vp-custom-block-info-text: var(--vp-c-text-1);
|
||||
--vp-custom-block-info-bg: var(--vp-c-default-soft);
|
||||
--vp-custom-block-info-code-bg: var(--vp-c-default-soft);
|
||||
|
||||
--vp-custom-block-tip-border: transparent;
|
||||
--vp-custom-block-tip-text: var(--vp-c-text-1);
|
||||
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
|
||||
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
|
||||
|
||||
--vp-custom-block-warning-border: transparent;
|
||||
--vp-custom-block-warning-text: var(--vp-c-text-1);
|
||||
--vp-custom-block-warning-bg: var(--vp-c-warning-soft);
|
||||
--vp-custom-block-warning-code-bg: var(--vp-c-warning-soft);
|
||||
|
||||
--vp-custom-block-danger-border: transparent;
|
||||
--vp-custom-block-danger-text: var(--vp-c-text-1);
|
||||
--vp-custom-block-danger-bg: var(--vp-c-danger-soft);
|
||||
--vp-custom-block-danger-code-bg: var(--vp-c-danger-soft);
|
||||
|
||||
--vp-custom-block-details-border: var(--vp-custom-block-info-border);
|
||||
--vp-custom-block-details-text: var(--vp-custom-block-info-text);
|
||||
--vp-custom-block-details-bg: var(--vp-custom-block-info-bg);
|
||||
--vp-custom-block-details-code-bg: var(--vp-custom-block-info-code-bg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Input
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-input-border-color: var(--vp-c-border);
|
||||
--vp-input-bg-color: var(--vp-c-bg-alt);
|
||||
|
||||
--vp-input-switch-bg-color: var(--vp-c-gray-soft);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Nav
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-nav-height: 64px;
|
||||
--vp-nav-bg-color: var(--vp-c-bg);
|
||||
--vp-nav-screen-bg-color: var(--vp-c-bg);
|
||||
--vp-nav-logo-height: 24px;
|
||||
}
|
||||
|
||||
.hide-nav {
|
||||
--vp-nav-height: 0px;
|
||||
}
|
||||
|
||||
.hide-nav .VPSidebar {
|
||||
--vp-nav-height: 22px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Local Nav
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-local-nav-bg-color: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Sidebar
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-sidebar-width: 272px;
|
||||
--vp-sidebar-bg-color: var(--vp-c-bg-alt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Colors Backdrop
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-backdrop-bg-color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Home
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-home-hero-name-color: var(--vp-c-brand-1);
|
||||
--vp-home-hero-name-background: transparent;
|
||||
|
||||
--vp-home-hero-image-background-image: none;
|
||||
--vp-home-hero-image-filter: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Badge
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-badge-info-border: transparent;
|
||||
--vp-badge-info-text: var(--vp-c-text-2);
|
||||
--vp-badge-info-bg: var(--vp-c-default-soft);
|
||||
|
||||
--vp-badge-tip-border: transparent;
|
||||
--vp-badge-tip-text: var(--vp-c-brand-1);
|
||||
--vp-badge-tip-bg: var(--vp-c-brand-soft);
|
||||
|
||||
--vp-badge-warning-border: transparent;
|
||||
--vp-badge-warning-text: var(--vp-c-warning-1);
|
||||
--vp-badge-warning-bg: var(--vp-c-warning-soft);
|
||||
|
||||
--vp-badge-danger-border: transparent;
|
||||
--vp-badge-danger-text: var(--vp-c-danger-1);
|
||||
--vp-badge-danger-bg: var(--vp-c-danger-soft);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Carbon Ads
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-carbon-ads-text-color: var(--vp-c-text-1);
|
||||
--vp-carbon-ads-poweredby-color: var(--vp-c-text-2);
|
||||
--vp-carbon-ads-bg-color: var(--vp-c-bg-soft);
|
||||
--vp-carbon-ads-hover-text-color: var(--vp-c-brand-1);
|
||||
--vp-carbon-ads-hover-poweredby-color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component: Local Search
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
:root {
|
||||
--vp-local-search-bg: var(--vp-c-bg);
|
||||
--vp-local-search-result-bg: var(--vp-c-bg);
|
||||
--vp-local-search-result-border: var(--vp-c-divider);
|
||||
--vp-local-search-result-selected-bg: var(--vp-c-bg);
|
||||
--vp-local-search-result-selected-border: var(--vp-c-brand-1);
|
||||
--vp-local-search-highlight-bg: var(--vp-c-brand-1);
|
||||
--vp-local-search-highlight-text: var(--vp-c-neutral-inverse);
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--vp-c-brand-soft);
|
||||
}
|
||||
|
||||
|
||||
133
v3-docs/docs/about/install.md
Normal file
133
v3-docs/docs/about/install.md
Normal file
@@ -0,0 +1,133 @@
|
||||
|
||||
# Install
|
||||
|
||||
You need to install the Meteor command line tool to create, run, and manage your Meteor.js projects. Check the prerequisites and follow the installation process below.
|
||||
|
||||
|
||||
## Prerequisites {#prereqs}
|
||||
|
||||
### Node.js version {#prereqs-node}
|
||||
|
||||
> Meteor 2.x runs on a deprecated Node.js version (14). Meteor 3.0 is in progress, and it will run on the latest Node.js version. For more information, please consult our [migration guide](https://guide.meteor.com/3.0-migration.html).
|
||||
|
||||
- Node.js version >= 10 and <= 14 is required.
|
||||
- We recommend you using [nvm](https://github.com/nvm-sh/nvm) or [Volta](https://volta.sh/) for managing Node.js versions.
|
||||
|
||||
|
||||
### Operating System (OS) {#prereqs-os}
|
||||
|
||||
- Meteor currently supports **OS X, Windows, and Linux**. Only 64-bit is supported.
|
||||
- Meteor supports Windows 7 / Windows Server 2008 R2 and up.
|
||||
- Apple M1 is natively supported from Meteor 2.5.1 onward (for older versions, rosetta terminal is required).
|
||||
- If you are on a Mac M1 (Arm64 version) you need to have Rosetta 2 installed, as Meteor uses it for running MongoDB. Check how to install it [here](https://osxdaily.com/2020/12/04/how-install-rosetta-2-apple-silicon-mac/).
|
||||
- Disabling antivirus (Windows Defender, etc.) will improve performance.
|
||||
- For compatibility, Linux binaries are built with CentOS 6.4 i386/amd64.
|
||||
|
||||
|
||||
### Mobile Development {#prereqs-mobile}
|
||||
|
||||
- iOS development requires the latest Xcode.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Install the latest official version of Meteor.js from your terminal by running one of the commands below. You can check our [changelog](https://docs.meteor.com/changelog.html) for the release notes.
|
||||
|
||||
> Run `node -v` to ensure you are using Node.js 14. Meteor 3.0 is in progress, and it will run on the latest Node.js version.
|
||||
|
||||
For Windows, Linux and OS X, you can run the following command:
|
||||
|
||||
```bash
|
||||
npm install -g meteor
|
||||
```
|
||||
|
||||
An alternative for Linux and OS X, is to install Meteor by using curl:
|
||||
|
||||
```bash
|
||||
curl https://install.meteor.com/ | sh
|
||||
```
|
||||
|
||||
You can also install a specific Meteor.js version by using curl:
|
||||
|
||||
```bash
|
||||
curl https://install.meteor.com/\?release\=2.8 | sh
|
||||
```
|
||||
|
||||
> Do not install the npm Meteor Tool in your project's package.json. This library is just an installer.
|
||||
|
||||
|
||||
## Troubleshooting {#troubleshooting}
|
||||
|
||||
If your user doesn't have permission to install global binaries, and you need to use sudo, it's necessary to append *--unsafe-perm* to the above command:
|
||||
|
||||
```bash
|
||||
sudo npm install -g meteor --unsafe-perm
|
||||
```
|
||||
|
||||
We strongly discourage the usage of Node.js or Meteor with root permissions.
|
||||
Only run the above command with sudo if you know what you are doing.
|
||||
|
||||
If you only use sudo because of a distribution default permission system, [check this link for fixing it](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally).
|
||||
|
||||
In some cases you can get this error `npm WARN checkPermissions Missing write access to /usr/local/lib/node_modules` because your Node.js installation was performed with wrong permissions. An easy way to fix this is to install Node.js using [nvm](https://github.com/nvm-sh/nvm) and forcing it to be used in your terminal. You can force it in the current session of your terminal by running `nvm use 14`.
|
||||
|
||||
|
||||
## PATH management {#path-management}
|
||||
|
||||
By default, the Meteor installer adds its install path (by default, `~/.meteor/`) to your PATH by updating either your `.bashrc`, `.bash_profile`, or `.zshrc` as appropriate. To disable this behavior, install Meteor by running:
|
||||
|
||||
```bash
|
||||
npm install -g meteor --ignore-meteor-setup-exec-path
|
||||
```
|
||||
|
||||
(or by setting the environment variable `npm_config_ignore_meteor_setup_exec_path=true`)
|
||||
|
||||
|
||||
## Old Versions on Apple M1 {#old-versions-m1}
|
||||
|
||||
For Apple M1 computers, you can append Rosetta prefix as following, if you need to run older versions of Meteor (before 2.5.1):
|
||||
|
||||
```bash
|
||||
arch -x86_64 npm install -g meteor
|
||||
```
|
||||
|
||||
or select Terminal in the Applications folder, press CMD(⌘)+I and check the "Open using Rosetta" option.
|
||||
|
||||
|
||||
## Meteor Docker image {#meteor-docker}
|
||||
|
||||
You can also use a Docker container for running Meteor inside your CI, or even in your local development toolchain.
|
||||
|
||||
We do provide the meteor/meteor-base ubuntu-based Docker image, that comes pre-bundled with Node.JS and Meteor, and runs it as a local user (not as root).
|
||||
|
||||
You can refer to our meteor/galaxy-images repository to see how to use it, and the latest version. [More about meteor-base here.](https://github.com/meteor/galaxy-images/blob/master/meteor-base/README.md)
|
||||
|
||||
|
||||
|
||||
## Note for Windows users {#windows}
|
||||
|
||||
On Windows, the installer runs faster when [Windows Developer Mode](https://docs.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development) is enabled. The installation extracts a large number of small files, which Windows Defender can cause to be very slow.
|
||||
|
||||
|
||||
|
||||
## Node version manager {#nvm}
|
||||
|
||||
If you use a node version manager that uses a separate global `node_modules` folder for each Node version, you will need to re-install the `meteor` npm package when changing to a Node version for the first time. Otherwise, the `meteor` command will no longer be found.
|
||||
|
||||
|
||||
## Note for fish shell users (Linux) {#fish-shell}
|
||||
|
||||
To be able to user `meteor` command from fish it's needed to include `/home/<user>/.meteor` in `$PATH`; to do that just add this line in `/home/<user>/.config/fish/config.fish` file (replace `<user>` with your username):
|
||||
|
||||
`set PATH /home/<user>/.meteor $PATH`
|
||||
|
||||
|
||||
## Uninstalling Meteor {#uninstall}
|
||||
|
||||
If you installed Meteor using npm, you can remove it by running:
|
||||
`meteor-installer uninstall`
|
||||
|
||||
If you installed Meteor using curl, you can remove it by running:
|
||||
`rm -rf ~/.meteor`
|
||||
`sudo rm /usr/local/bin/meteor`
|
||||
|
||||
84
v3-docs/docs/about/roadmap.md
Normal file
84
v3-docs/docs/about/roadmap.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Roadmap
|
||||
|
||||
Describes the high-level features and actions for the Meteor project in the near-to-medium term future.
|
||||
|
||||
## Introduction
|
||||
|
||||
**Quick update moving items to Finished: June 1, 2023**
|
||||
|
||||
**Last new items added: September 14, 2022.**
|
||||
|
||||
The description of many items include sentences and ideas from Meteor community members.
|
||||
|
||||
Contributors are encouraged to focus their efforts on work that aligns with the roadmap then we can work together in these areas.
|
||||
|
||||
|
||||
> As with any roadmap, this is a living document that will evolve as priorities and dependencies shift.
|
||||
|
||||
> If you have new feature requests or ideas you should open a new [discussion](https://github.com/meteor/meteor/discussions/new).
|
||||
|
||||
### Meteor 3.0 release
|
||||
|
||||
- Change how Meteor executes Async code; ([Discussion](https://github.com/meteor/meteor/discussions/11505))
|
||||
- Provide new async APIs where Fibers are required;
|
||||
- Mongo package with Async API; ([PR](https://github.com/meteor/meteor/pull/12028))
|
||||
- Provide async versions for Accounts and core packages;
|
||||
- Adapt Meteor Promise implementation;
|
||||
- Enable Top-Level Await (TLA) on Meteor server-side; ([PR](https://github.com/meteor/meteor/pull/12095))
|
||||
- Support Top-Level Await (TLA) on Reify;
|
||||
- Remove Fibers dependency from Meteor Public APIs;
|
||||
- Remove Fibers entirely;
|
||||
- Update Cordova integration to Meteor 3.0;
|
||||
- Run Meteor on Node.js 18;
|
||||
- Change web engine from Connect to Express;
|
||||
|
||||
### Next releases
|
||||
|
||||
- Improve TypeScript support for Meteor and packages; ([Discussion](https://github.com/meteor/meteor/discussions/12080))
|
||||
- Linux ARM Support; ([PR](https://github.com/meteor/meteor/pull/11809))
|
||||
- Improve release quality with test coverage and CI automation;
|
||||
- Review and help to modernize Meteor tools; ([Discussion](https://github.com/meteor/meteor/discussions/12073))
|
||||
- Improve support for Windows 11 or adopt Windows with WSL;
|
||||
- Improve Meteor build time; ([Discussion](https://github.com/meteor/meteor/discussions/11587))
|
||||
- HTTP/3 Support;
|
||||
- Tree-shaking; ([PR](https://github.com/meteor/meteor/pull/11164))
|
||||
- Support package.json exports fields; ([Discussion](https://github.com/meteor/meteor/discussions/11727))
|
||||
|
||||
### Candidate items
|
||||
We need to discuss further to decide whether or not to proceed with these implementations.
|
||||
|
||||
- Update and fix Meteor Client Bundler or Improve DDP Client;
|
||||
- Improve Passwordless package; ([Discussion](https://github.com/meteor/meteor/discussions/12075))
|
||||
- Support building mobile apps using CapacitorJS;
|
||||
- Bring Redis-oplog to core; ([Repository](https://github.com/Meteor-Community-Packages/redis-oplog))
|
||||
- MongoDB Change Streams support; ([Discussion](https://github.com/meteor/meteor/discussions/11842))
|
||||
- Better file upload support via DDP; ([Discussion](https://github.com/meteor/meteor/discussions/11523))
|
||||
|
||||
### Next educational items
|
||||
|
||||
- Create a new Meteor Guide; ([Current Guide](https://guide.meteor.com/))
|
||||
- Scaling Meteor Apps course; ([Meteor University](https://university.meteor.com/))
|
||||
|
||||
### Finished items
|
||||
|
||||
- New Async Tracker; ([Blog Post](https://blog.meteor.com/new-meteor-js-2-10-and-the-async-tracker-feature-ffdbe817c801))
|
||||
- New Suspense hooks for React + Meteor; ([Blog Post](https://blog.meteor.com/new-suspense-hooks-for-meteor-5391570b3007))
|
||||
- Release Blaze 2.7 supporting async calls; ([Changelog](https://www.blazejs.org/changelog.html))
|
||||
- New Scaffold API / generate command; ([Blog Post](https://blog.meteor.com/new-meteor-2-9-and-the-scaffold-api-8b5b2b2b2b2b))
|
||||
- Types added to the core; ([Blog Post](https://blog.meteor.com/new-meteor-2-8-1-and-adding-types-to-the-core-8a6ee56f0141))
|
||||
- Update Apollo skeleton NPM dependencies;
|
||||
- MongoDB 6.0 Support; ([Discussion](https://github.com/meteor/meteor/discussions/12092) / [Blog Post](https://blog.meteor.com/new-meteor-2-11-and-the-new-embedded-mongodb-19767076961b))
|
||||
- Vite integration;
|
||||
- SolidJS integration;
|
||||
- Vue 3 integration; ([Forums](https://forums.meteor.com/t/status-of-vue-3-meteor/57915/25) / [Discussion](https://github.com/meteor/meteor/discussions/11521))
|
||||
- SolidJS starter template;
|
||||
- Login and Accounts Course; ([Meteor University](https://university.meteor.com/))
|
||||
- Updated MongoDB driver to 4.8; ([PR](https://github.com/meteor/meteor/pull/12097))
|
||||
- Make MongoDB integration stable by fixing critical issues;
|
||||
- New skeleton for creating Meteor apps with Chakra UI;
|
||||
- Evaluate and improve support for Meteor in VSCode; ([Repository](https://github.com/matheusccastroo/vscode-meteor-toolbox))
|
||||
- Release Blaze 2.6.2; ([Blog Post](https://blog.meteor.com/new-meteor-js-2-12-and-the-blaze-2-6-2-release-b72c2a7a593f))
|
||||
|
||||
-----------
|
||||
|
||||
For more completed items, refer to our [changelog](https://docs.meteor.com/changelog.html).
|
||||
33
v3-docs/docs/about/what-is.md
Normal file
33
v3-docs/docs/about/what-is.md
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
|
||||
> Meteor 2.x runs on a deprecated Node.js version (14). Meteor 3.0 is in progress, and it will run on the latest Node.js version. For more information, please consult our [migration guide](https://guide.meteor.com/3.0-migration.html).
|
||||
|
||||
|
||||
# What is Meteor? {#what-is-meteor}
|
||||
|
||||
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.
|
||||
|
||||
- Meteor allows you to develop in **one language**, JavaScript, in all environments: application server, web browser, and mobile device.
|
||||
|
||||
- Meteor uses **data on the wire**, meaning the server sends data, not HTML, and the client renders it.
|
||||
|
||||
- Meteor **embraces the ecosystem**, bringing the best parts of the extremely active JavaScript community to you in a careful and considered way.
|
||||
|
||||
- Meteor provides **full stack reactivity**, allowing your UI to seamlessly reflect the true state of the world with minimal development effort.
|
||||
|
||||
## Meteor resources {#learning-more}
|
||||
|
||||
1. First, learn how to install meteor in the [installation section](/about/install.html).
|
||||
|
||||
1. The place to get started with Meteor is the [tutorials page](https://www.meteor.com/developers/tutorials).
|
||||
|
||||
1. [Meteor Examples](https://github.com/meteor/examples) is a list of examples using Meteor. You can also include your example with Meteor.
|
||||
|
||||
1. 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.
|
||||
|
||||
1. Visit the [Meteor discussion forums](https://forums.meteor.com) to announce projects, get help, talk about the community, or discuss changes to core.
|
||||
|
||||
1. [Meteor Slack Community](https://join.slack.com/t/meteor-community/shared_invite/enQtODA0NTU2Nzk5MTA3LWY5NGMxMWRjZDgzYWMyMTEyYTQ3MTcwZmU2YjM5MTY3MjJkZjQ0NWRjOGZlYmIxZjFlYTA5Mjg4OTk3ODRiOTc) is the best place to ask (and answer!) technical questions and also meet Meteor developers.
|
||||
|
||||
1. [Atmosphere](https://atmospherejs.com) is the repository of community packages designed especially for Meteor.
|
||||
|
||||
49
v3-docs/docs/api-examples.md
Normal file
49
v3-docs/docs/api-examples.md
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Runtime API Examples
|
||||
|
||||
This page demonstrates usage of some of the runtime APIs provided by VitePress.
|
||||
|
||||
The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` and `.vue` files:
|
||||
|
||||
```md
|
||||
<script setup>
|
||||
import { useData } from 'vitepress'
|
||||
|
||||
const { theme, page, frontmatter } = useData()
|
||||
</script>
|
||||
|
||||
## Results
|
||||
|
||||
### Theme Data
|
||||
<pre>{{ theme }}</pre>
|
||||
|
||||
### Page Data
|
||||
<pre>{{ page }}</pre>
|
||||
|
||||
### Page Frontmatter
|
||||
<pre>{{ frontmatter }}</pre>
|
||||
```
|
||||
|
||||
<script setup>
|
||||
import { useData } from 'vitepress'
|
||||
|
||||
const { site, theme, page, frontmatter } = useData()
|
||||
</script>
|
||||
|
||||
## Results
|
||||
|
||||
### Theme Data
|
||||
<pre>{{ theme }}</pre>
|
||||
|
||||
### Page Data
|
||||
<pre>{{ page }}</pre>
|
||||
|
||||
### Page Frontmatter
|
||||
<pre>{{ frontmatter }}</pre>
|
||||
|
||||
## More
|
||||
|
||||
Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata).
|
||||
81
v3-docs/docs/api/DDPRateLimiter.md
Normal file
81
v3-docs/docs/api/DDPRateLimiter.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# DDPRateLimiter
|
||||
|
||||
Customize rate limiting for methods and subscriptions to avoid a high load of WebSocket messages in your app.
|
||||
|
||||
> 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).
|
||||
|
||||
By default, `DDPRateLimiter` is configured with a single rule. This rule
|
||||
limits login attempts, new user creation, and password resets to 5 attempts
|
||||
every 10 seconds per connection. It can be removed by calling
|
||||
`Accounts.removeDefaultRateLimit()`.
|
||||
|
||||
To use `DDPRateLimiter` for modifying the default rate-limiting rules,
|
||||
add the `ddp-rate-limiter` package to your project in your terminal:
|
||||
|
||||
```bash
|
||||
meteor add ddp-rate-limiter
|
||||
```
|
||||
|
||||
<ApiBox name="DDPRateLimiter.addRule" hasCustomExample/>
|
||||
|
||||
Custom rules can be added by calling `DDPRateLimiter.addRule`. The rate
|
||||
limiter is called on every method and subscription invocation.
|
||||
|
||||
A rate limit is reached when a bucket has surpassed the rule's predefined
|
||||
capacity, at which point errors will be returned for that input until the
|
||||
buckets are reset. Buckets are regularly reset after the end of a time
|
||||
interval.
|
||||
|
||||
|
||||
Here's example of defining a rule and adding it into the `DDPRateLimiter`:
|
||||
```js
|
||||
// Define a rule that matches login attempts by non-admin users.
|
||||
const loginRule = {
|
||||
userId(userId) {
|
||||
const user = Meteor.users.findOne(userId);
|
||||
return user && user.type !== 'admin';
|
||||
},
|
||||
|
||||
type: 'method',
|
||||
name: 'login'
|
||||
};
|
||||
|
||||
// Add the rule, allowing up to 5 messages every 1000 milliseconds.
|
||||
DDPRateLimiter.addRule(loginRule, 5, 1000);
|
||||
|
||||
```
|
||||
|
||||
<ApiBox name="DDPRateLimiter.removeRule" />
|
||||
<ApiBox name="DDPRateLimiter.setErrorMessage" />
|
||||
<ApiBox name="DDPRateLimiter.setErrorMessageOnRule" />
|
||||
|
||||
Allows developers to specify custom error messages for each rule instead of being
|
||||
limited to one global error message for every rule.
|
||||
It adds some clarity to what rules triggered which errors, allowing for better UX
|
||||
and also opens the door for i18nable error messages per rule instead of the
|
||||
default English error message.
|
||||
|
||||
Here is an example with a custom error message:
|
||||
```js
|
||||
const setupGoogleAuthenticatorRule = {
|
||||
userId(userId) {
|
||||
const user = Meteor.users.findOne(userId);
|
||||
return user;
|
||||
},
|
||||
type: 'method',
|
||||
name: 'Users.setupGoogleAuthenticator',
|
||||
};
|
||||
|
||||
// Add the rule, allowing up to 1 google auth setup message every 60 seconds
|
||||
const ruleId = DDPRateLimiter.addRule(setupGoogleAuthenticatorRule, 1, 60000);
|
||||
DDPRateLimiter.setErrorMessageOnRule(ruleId, function (data) {
|
||||
return `You have reached the maximum number of Google Authenticator attempts. Please try again in ${Math.ceil(data.timeToReset / 1000)} seconds.`;
|
||||
});
|
||||
```
|
||||
|
||||
Or a more simple approach:
|
||||
|
||||
```js
|
||||
const ruleId = DDPRateLimiter.addRule(setupGoogleAuthenticatorRule, 1, 60000);
|
||||
DDPRateLimiter.setErrorMessageOnRule(ruleId, 'Example as a single string error message');
|
||||
```
|
||||
130
v3-docs/docs/api/EJSON.md
Normal file
130
v3-docs/docs/api/EJSON.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# EJSON
|
||||
|
||||
EJSON is an extension of JSON to support more types. It supports all JSON-safe types, as well as:
|
||||
|
||||
- **Date** (JavaScript `Date`)
|
||||
- **Binary** (JavaScript `Uint8Array` or the
|
||||
result of [`EJSON.newBinary`](#EJSON-newBinary))
|
||||
- **Special numbers** (JavaScript `NaN`, `Infinity`, and `-Infinity`)
|
||||
- **Regular expressions** (JavaScript `RegExp`)
|
||||
- **User-defined types** (see [`EJSON.addType`](#EJSON-addType). For example,
|
||||
[`Mongo.ObjectID`](./collections.md#Mongo-ObjectID) is implemented this way.)
|
||||
|
||||
All EJSON serializations are also valid JSON. For example an object with a date
|
||||
and a binary buffer would be serialized in EJSON as:
|
||||
|
||||
```json
|
||||
{
|
||||
"d": { "$date": 1358205756553 },
|
||||
"b": { "$binary": "c3VyZS4=" }
|
||||
}
|
||||
```
|
||||
|
||||
Meteor supports all built-in EJSON data types in publishers, method arguments
|
||||
and results, Mongo databases, and [`Session`](./session.md) variables.
|
||||
|
||||
<ApiBox name="EJSON.parse" />
|
||||
|
||||
<ApiBox name="EJSON.stringify" hasCustomExample/>
|
||||
|
||||
```js
|
||||
import { EJSON } from "meteor/ejson";
|
||||
|
||||
EJSON.stringify(
|
||||
{ num: 42, someProp: "foo" },
|
||||
options // this param is optional
|
||||
);
|
||||
```
|
||||
|
||||
<ApiBox name="EJSON.fromJSONValue" />
|
||||
|
||||
<ApiBox name="EJSON.toJSONValue" />
|
||||
|
||||
<ApiBox name="EJSON.equals" />
|
||||
|
||||
<ApiBox name="EJSON.clone" />
|
||||
|
||||
<ApiBox name="EJSON.newBinary" />
|
||||
|
||||
Buffers of binary data are represented by `Uint8Array` instances on JavaScript
|
||||
platforms that support them. On implementations of JavaScript that do not
|
||||
support `Uint8Array`, binary data buffers are represented by standard arrays
|
||||
containing numbers ranging from 0 to 255, and the `$Uint8ArrayPolyfill` key
|
||||
set to `true`.
|
||||
|
||||
<ApiBox name="EJSON.isBinary" />
|
||||
|
||||
<ApiBox name="EJSON.addType" hasCustomExample/>
|
||||
|
||||
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:
|
||||
|
||||
```js
|
||||
class Distance {
|
||||
constructor(value, unit) {
|
||||
this.value = value;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
// Convert our type to JSON.
|
||||
toJSONValue() {
|
||||
return {
|
||||
value: this.value,
|
||||
unit: this.unit,
|
||||
};
|
||||
}
|
||||
|
||||
// Unique type name.
|
||||
typeName() {
|
||||
return "Distance";
|
||||
}
|
||||
}
|
||||
|
||||
EJSON.addType("Distance", function fromJSONValue(json) {
|
||||
return new Distance(json.value, json.unit);
|
||||
});
|
||||
|
||||
EJSON.stringify(new Distance(10, "m"));
|
||||
// Returns '{"$type":"Distance","$value":{"value":10,"unit":"m"}}'
|
||||
```
|
||||
|
||||
When you add a type to EJSON, Meteor will be able to use that type in:
|
||||
|
||||
- publishing objects of your type if you pass them to publish handlers.
|
||||
- allowing your type in the return values or arguments to
|
||||
[methods](./meteor.md#methods).
|
||||
- storing your type client-side in Minimongo.
|
||||
- allowing your type in [`Session`](./session.md) variables.
|
||||
|
||||
Instances of your type must implement [`typeName`](#EJSON-CustomType-typeName) and
|
||||
[`toJSONValue`](#EJSON-CustomType-toJSONValue) methods, and may implement
|
||||
[`clone`](#EJSON-CustomType-clone) and [`equals`](#EJSON-CustomType-equals) methods if the
|
||||
default implementations are not sufficient.
|
||||
|
||||
<ApiBox name="EJSON.CustomType#typeName" hasCustomExample instanceName="CustomType"/>
|
||||
<ApiBox name="EJSON.CustomType#toJSONValue" hasCustomExample instanceName="CustomType"/>
|
||||
|
||||
For example, the `toJSONValue` method for
|
||||
[`Mongo.ObjectID`](./collections.md#Mongo-ObjectID) could be:
|
||||
|
||||
```js
|
||||
function () {
|
||||
return this.toHexString();
|
||||
}
|
||||
```
|
||||
|
||||
<ApiBox name="EJSON.CustomType#clone" hasCustomExample instanceName="CustomType"/>
|
||||
|
||||
If your type does not have a `clone` method, `EJSON.clone` will use
|
||||
[`toJSONValue`](#EJSON-CustomType-toJSONValue) and the factory instead.
|
||||
|
||||
<ApiBox name="EJSON.CustomType#equals" hasCustomExample instanceName="CustomType"/>
|
||||
|
||||
The `equals` method should define an [equivalence relation](http://en.wikipedia.org/wiki/Equivalence_relation). It should have
|
||||
the following properties:
|
||||
|
||||
- _Reflexivity_ - for any instance `a`: `a.equals(a)` must be true.
|
||||
- _Symmetry_ - for any two instances `a` and `b`: `a.equals(b)` if and only if `b.equals(a)`.
|
||||
- _Transitivity_ - for any three instances `a`, `b`, and `c`: `a.equals(b)` and `b.equals(c)` implies `a.equals(c)`.
|
||||
|
||||
If your type does not have an `equals` method, `EJSON.equals` will compare the
|
||||
result of calling [`toJSONValue`](#EJSON-CustomType-toJSONValue) instead.
|
||||
119
v3-docs/docs/api/ReactiveDict.md
Normal file
119
v3-docs/docs/api/ReactiveDict.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Reactive Dict
|
||||
|
||||
A ReactiveDict stores an arbitrary set of key-value pairs. Use it to manage
|
||||
internal state in your components, ie. like the currently selected item in a list.
|
||||
Each key is individully reactive such that calling `set` for a key will
|
||||
invalidate any Computations that called `get` with that key, according to the
|
||||
usual contract for reactive data sources.
|
||||
|
||||
That means if you call [`ReactiveDict#get`](#ReactiveDict-get)`('currentList')`
|
||||
from inside a Blaze template helper, the template will automatically be rerendered
|
||||
whenever [`ReactiveDict#set`](#ReactiveDict-set)`('currentList', x)` is called.
|
||||
|
||||
To use `ReactiveDict`, add the `reactive-dict` package to your project by running
|
||||
in your terminal:
|
||||
|
||||
```bash
|
||||
meteor add reactive-dict
|
||||
```
|
||||
|
||||
|
||||
<ApiBox name="ReactiveDict" />
|
||||
|
||||
If you provide a name to its constructor, its contents will be saved across Hot
|
||||
Code Push client code updates.
|
||||
|
||||
<ApiBox name="ReactiveDict#set" instanceName="reactiveDict" hasCustomExample/>
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
import { ReactiveDict } from "meteor/reactive-dict";
|
||||
import { Tracker } from "meteor/tracker";
|
||||
import { Meteor } from "meteor/meteor";
|
||||
|
||||
const state = new ReactiveDict();
|
||||
state.set("currentRoomId", "random");
|
||||
|
||||
Tracker.autorun(() => {
|
||||
Meteor.subscribe("chatHistory", { room: state.get("currentRoomId") });
|
||||
});
|
||||
|
||||
// Causes the function passed to `Tracker.autorun` to be rerun, so that the
|
||||
// 'chatHistory' subscription is moved to the room 'general'.
|
||||
state.set("currentRoomId", "general");
|
||||
```
|
||||
|
||||
`ReactiveDict.set` can also be called with an object of keys and values, which is
|
||||
equivalent to calling `ReactiveDict.set` individually on each key/value pair.
|
||||
|
||||
```js
|
||||
import { ReactiveDict } from "meteor/reactive-dict";
|
||||
const state = new ReactiveDict();
|
||||
state.set({
|
||||
a: "foo",
|
||||
b: "bar",
|
||||
});
|
||||
```
|
||||
|
||||
<ApiBox name="ReactiveDict#setDefault" instanceName="reactiveDict" />
|
||||
|
||||
This is useful in initialization code, to avoid re-initializing your state
|
||||
every time a new version of your app is loaded.
|
||||
|
||||
<ApiBox name="ReactiveDict#get" instanceName="reactiveDict" />
|
||||
|
||||
Example in Blaze:
|
||||
|
||||
::: code-group
|
||||
|
||||
```html [main.html]
|
||||
<template name="main">
|
||||
<p>We've always been at war with {{theEnemy}}.</p>
|
||||
<button class="change-enemy">Change Enemy</button>
|
||||
</template>
|
||||
```
|
||||
|
||||
```js [main.js]
|
||||
Template.main.onCreated(function () {
|
||||
this.state = new ReactiveDict();
|
||||
this.state.set("enemy", "Eastasia");
|
||||
});
|
||||
Template.main.helpers({
|
||||
theEnemy() {
|
||||
const inst = Template.instance();
|
||||
return inst.state.get("enemy");
|
||||
},
|
||||
});
|
||||
Template.main.events({
|
||||
"click .change-enemy"(event, inst) {
|
||||
inst.state.set("enemy", "Eurasia");
|
||||
},
|
||||
});
|
||||
|
||||
// Clicking the button will change the page to say "We've always been at war with Eurasia"
|
||||
```
|
||||
:::
|
||||
|
||||
<ApiBox name="ReactiveDict#delete" instanceName="reactiveDict" />
|
||||
|
||||
<ApiBox name="ReactiveDict#equals" instanceName="reactiveDict" />
|
||||
|
||||
If value is a scalar, then these two expressions do the same thing:
|
||||
|
||||
```js
|
||||
import { ReactiveDict } from "meteor/reactive-dict";
|
||||
const state = new ReactiveDict();
|
||||
// ...
|
||||
state.get("key") === value;
|
||||
state.equals("key", value);
|
||||
```
|
||||
|
||||
However, the second is recommended, as it triggers fewer invalidations
|
||||
(template redraws), making your program more efficient.
|
||||
|
||||
<ApiBox name="ReactiveDict#all" instanceName="reactiveDict" />
|
||||
|
||||
<ApiBox name="ReactiveDict#clear" instanceName="reactiveDict" />
|
||||
|
||||
<ApiBox name="ReactiveDict#destroy" instanceName="reactiveDict" />
|
||||
41
v3-docs/docs/api/ReactiveVar.md
Normal file
41
v3-docs/docs/api/ReactiveVar.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# ReactiveVar
|
||||
|
||||
To use `ReactiveVar`, add the `reactive-var` package to your project by running
|
||||
in your terminal:
|
||||
|
||||
```bash
|
||||
meteor add reactive-var
|
||||
```
|
||||
|
||||
<ApiBox name="ReactiveVar" />
|
||||
|
||||
|
||||
A ReactiveVar holds a single value that can be get and set, such that calling
|
||||
`set` will invalidate any Computations that called `get`, according to the
|
||||
usual contract for reactive data sources.
|
||||
|
||||
A ReactiveVar is similar to a Session variable, with a few differences:
|
||||
|
||||
- ReactiveVars don't have global names, like the "foo" in `Session.get('foo')`.
|
||||
Instead, they may be created and used locally, for example attached to a
|
||||
template instance, as in: `this.foo.get()`.
|
||||
|
||||
- ReactiveVars are not automatically migrated across hot code pushes,
|
||||
whereas Session state is.
|
||||
|
||||
- ReactiveVars can hold any value, while Session variables are limited to
|
||||
JSON or EJSON.
|
||||
|
||||
An important property of ReactiveVars — which is sometimes a
|
||||
reason for using one — is that setting the value to the same
|
||||
value as before has no effect; it does not trigger any invalidations.
|
||||
So if one autorun sets a ReactiveVar, and another autorun gets the
|
||||
ReactiveVar, a re-run of the first autorun won't necessarily trigger
|
||||
the second. By default, only primitive values are compared this way,
|
||||
while calling `set` on an argument that is an _object_ (not a
|
||||
primitive) always counts as a change. You can configure this behavior
|
||||
using the `equalsFunc` argument.
|
||||
|
||||
<ApiBox name="ReactiveVar#get" instanceName="reactiveVar" />
|
||||
|
||||
<ApiBox name="ReactiveVar#set" instanceName="reactiveVar" />
|
||||
410
v3-docs/docs/api/Tracker.md
Normal file
410
v3-docs/docs/api/Tracker.md
Normal file
@@ -0,0 +1,410 @@
|
||||
# Tracker
|
||||
|
||||
Meteor has a simple dependency tracking system which allows it to
|
||||
automatically rerun templates and other computations whenever
|
||||
[`Session`](./session.md) variables, database queries, and other data
|
||||
sources change.
|
||||
|
||||
Unlike most other systems, you don't have to manually declare these
|
||||
dependencies & it "just works". The mechanism is simple and
|
||||
efficient. When you call a function that supports reactive updates
|
||||
(such as a database query), it automatically saves the current
|
||||
Computation object, if any (representing, for example, the current
|
||||
template being rendered). Later, when the data changes, the function
|
||||
can "invalidate" the Computation, causing it to rerun (rerendering the
|
||||
template).
|
||||
|
||||
Applications will find [`Tracker.autorun`](#Tracker-autorun) useful, while more
|
||||
advanced facilities such as `Tracker.Dependency` and `onInvalidate`
|
||||
callbacks are intended primarily for package authors implementing new
|
||||
reactive data sources.
|
||||
|
||||
<ApiBox name="Tracker.autorun" hasCustomExample/>
|
||||
|
||||
`Tracker.autorun` allows you to run a function that depends on reactive data
|
||||
sources, in such a way that if there are changes to the data later,
|
||||
the function will be rerun.
|
||||
|
||||
For example, you can monitor a cursor (which is a reactive data
|
||||
source) and aggregate it into a session variable:
|
||||
|
||||
```js
|
||||
Tracker.autorun(() => {
|
||||
const oldest = _.max(Monkeys.find().fetch(), (monkey) => {
|
||||
return monkey.age;
|
||||
});
|
||||
|
||||
if (oldest) {
|
||||
Session.set('oldest', oldest.name);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Or you can wait for a session variable to have a certain value, and do
|
||||
something the first time it does, calling `stop` on the computation to
|
||||
prevent further rerunning:
|
||||
|
||||
```js
|
||||
Tracker.autorun((computation) => {
|
||||
if (!Session.equals('shouldAlert', true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
computation.stop();
|
||||
alert('Oh no!');
|
||||
});
|
||||
```
|
||||
|
||||
The function is invoked immediately, at which point it may alert and
|
||||
stop right away if `shouldAlert` is already true. If not, the
|
||||
function is run again when `shouldAlert` becomes true.
|
||||
|
||||
A change to a data dependency does not cause an immediate rerun, but
|
||||
rather "invalidates" the computation, causing it to rerun the next
|
||||
time a flush occurs. A flush will occur automatically as soon as
|
||||
the system is idle if there are invalidated computations. You can
|
||||
also use [`Tracker.flush`](#Tracker-flush) to cause an immediate flush of
|
||||
all pending reruns.
|
||||
|
||||
If you nest calls to `Tracker.autorun`, then when the outer call stops or
|
||||
reruns, the inner call will stop automatically. Subscriptions and
|
||||
observers are also automatically stopped when used as part of a
|
||||
computation that is rerun, allowing new ones to be established. See
|
||||
[`Meteor.subscribe`](./meteor.md#Meteor-subscribe) for more information about
|
||||
subscriptions and reactivity.
|
||||
|
||||
If the initial run of an autorun throws an exception, the computation
|
||||
is automatically stopped and won't be rerun.
|
||||
|
||||
### Tracker.autorun and async callbacks
|
||||
`Tracker.autorun` can accept an `async` callback function.
|
||||
To preserve reactivity for the reactive variables inside the async callback function, you must use a `Tracker.withComputation` call as described below:
|
||||
|
||||
<ApiBox name="Tracker.withComputation" />
|
||||
|
||||
```javascript
|
||||
Tracker.autorun(async function example1(computation) {
|
||||
// Code before the first await will stay reactive.
|
||||
reactiveVar1.get(); // This will trigger a rerun.
|
||||
|
||||
let links = await LinksCollection.findAsync({}).fetch(); // First async call will stay reactive.
|
||||
|
||||
// Code after the first await looses Tracker.currentComputation: no reactivity.
|
||||
reactiveVar2.get(); // This won't trigger a rerun.
|
||||
|
||||
// You can bring back reactivity with the Tracker.withCompuation wrapper:
|
||||
let users = await Tracker.withComputation(computation, () => Meteor.users.findAsync({}).fetch());
|
||||
|
||||
// Code below will again not be reactive, so you will need another Tracker.withComputation.
|
||||
const value = Tracker.withComputation(computation, () => reactiveVar3.get()); // This will trigger a rerun.
|
||||
});
|
||||
```
|
||||
|
||||
As a rule of thumb, you are okay with wrapping all reactive statements inside a `Tracker.withComputation` to preserve current computation.
|
||||
But it comes at a performance cost - it should be used only where needed.
|
||||
|
||||
Reason behind is, that an await implicitly *"moves"* the code below in a Promise resolved function. When this function runs (after it has been fetched from the micro task queue), `Tracker.withComputation` preserves the reference to the computation of the `Tracker.autorun`.
|
||||
|
||||
The `react-meteor-data` package uses `Tracker.withComputation` to make the `useTracker` accept async callbacks.
|
||||
More can be seen [here](https://github.com/meteor/react-packages/tree/master/packages/react-meteor-data#maintaining-the-reactive-context)
|
||||
|
||||
### Using async callbacks in versions of Meteor prior to 2.10
|
||||
`Tracker.autorun` can accept an `async` callback function.
|
||||
However, the async call back function will only be dependent on reactive functions called prior to any called functions that return a promise.
|
||||
|
||||
Example 1 - autorun `example1()` **is not** dependent on reactive changes to the `Meteor.users` collection. Because it is dependent on nothing reactive it will run only once:
|
||||
```javascript
|
||||
Tracker.autorun(async function example1() {
|
||||
let asyncData = await asyncDataFunction();
|
||||
let users = Meteor.users.find({}).fetch();
|
||||
});
|
||||
```
|
||||
|
||||
However, simply changing the order so there are no `async` calls prior to the reactive call to `Meteor.users.find`, will make the async autorun `example2()` dependent on reactive changes to the `Meteor.users` collection.
|
||||
|
||||
Example 2 - autorun `example2()` **is** dependent on reactive changes to the Meteor.users collection. Changes to the `Meteor.users` collection will cause a rerun of `example2()`:
|
||||
```javascript
|
||||
Tracker.autorun(async function example2() {
|
||||
let users = Meteor.users.find({}).fetch();
|
||||
let asyncData = await asyncDataFunction();
|
||||
});
|
||||
```
|
||||
<ApiBox name="Tracker.flush" />
|
||||
|
||||
Normally, when you make changes (like writing to the database),
|
||||
their impact (like updating the DOM) is delayed until the system is
|
||||
idle. This keeps things predictable — you can know that the DOM
|
||||
won't go changing out from under your code as it runs. It's also one
|
||||
of the things that makes Meteor fast.
|
||||
|
||||
`Tracker.flush` forces all of the pending reactive updates to complete.
|
||||
For example, if an event handler changes a Session
|
||||
variable that will cause part of the user interface to rerender, the
|
||||
handler can call `flush` to perform the rerender immediately and then
|
||||
access the resulting DOM.
|
||||
|
||||
An automatic flush occurs whenever the system is idle which performs
|
||||
exactly the same work as `Tracker.flush`. The flushing process consists
|
||||
of rerunning any invalidated computations. If additional
|
||||
invalidations happen while flushing, they are processed as part of the
|
||||
same flush until there is no more work to be done. Callbacks
|
||||
registered with [`Tracker.afterFlush`](#Tracker-afterFlush) are called
|
||||
after processing outstanding invalidations.
|
||||
|
||||
It is illegal to call `flush` from inside a `flush` or from a running
|
||||
computation.
|
||||
|
||||
The [Tracker manual](https://github.com/meteor/docs/blob/master/long-form/tracker-manual.md#the-flush-cycle)
|
||||
describes the motivation for the flush cycle and the guarantees made by
|
||||
`Tracker.flush` and `Tracker.afterFlush`.
|
||||
|
||||
<ApiBox name="Tracker.nonreactive" />
|
||||
|
||||
Calls `func` with `Tracker.currentComputation` temporarily set to `null`
|
||||
and returns `func`'s own return value. If `func` accesses reactive data
|
||||
sources, these data sources will never cause a rerun of the enclosing
|
||||
computation.
|
||||
|
||||
<ApiBox name="Tracker.active" />
|
||||
|
||||
This value is useful for data source implementations to determine
|
||||
whether they are being accessed reactively or not.
|
||||
|
||||
<ApiBox name="Tracker.inFlush" />
|
||||
|
||||
This value indicates, whether a flush is in progress or not.
|
||||
|
||||
<ApiBox name="Tracker.currentComputation" />
|
||||
|
||||
It's very rare to need to access `currentComputation` directly. The
|
||||
current computation is used implicitly by
|
||||
[`Tracker.active`](#Tracker-active) (which tests whether there is one),
|
||||
[`dependency.depend()`](#Tracker-Dependency-depend) (which registers that it depends on a
|
||||
dependency), and [`Tracker.onInvalidate`](#Tracker-onInvalidate) (which
|
||||
registers a callback with it).
|
||||
|
||||
<ApiBox name="Tracker.onInvalidate" />
|
||||
|
||||
See [`computation.onInvalidate`](#Tracker-Computation-onInvalidate) for more
|
||||
details.
|
||||
|
||||
<ApiBox name="Tracker.afterFlush" />
|
||||
|
||||
Functions scheduled by multiple calls to `afterFlush` are guaranteed
|
||||
to run in the order that `afterFlush` was called. Functions are
|
||||
guaranteed to be called at a time when there are no invalidated
|
||||
computations that need rerunning. This means that if an `afterFlush`
|
||||
function invalidates a computation, that computation will be rerun
|
||||
before any other `afterFlush` functions are called.
|
||||
|
||||
|
||||
## Tracker.Computation {#tracker_computation}
|
||||
|
||||
A Computation object represents code that is repeatedly rerun in
|
||||
response to reactive data changes. Computations don't have return
|
||||
values; they just perform actions, such as rerendering a template on
|
||||
the screen. Computations are created using [`Tracker.autorun`](#Tracker-autorun).
|
||||
Use [`stop`](#Tracker-Computation-stop) to prevent further rerunning of a
|
||||
computation.
|
||||
|
||||
Each time a computation runs, it may access various reactive data
|
||||
sources that serve as inputs to the computation, which are called its
|
||||
dependencies. At some future time, one of these dependencies may
|
||||
trigger the computation to be rerun by invalidating it. When this
|
||||
happens, the dependencies are cleared, and the computation is
|
||||
scheduled to be rerun at flush time.
|
||||
|
||||
The *current computation*
|
||||
([`Tracker.currentComputation`](#Tracker-currentComputation)) is the
|
||||
computation that is currently being run or rerun (computed), and the
|
||||
one that gains a dependency when a reactive data source is accessed.
|
||||
Data sources are responsible for tracking these dependencies using
|
||||
[`Tracker.Dependency`](#Tracker-Dependency) objects.
|
||||
|
||||
Invalidating a computation sets its `invalidated` property to true
|
||||
and immediately calls all of the computation's `onInvalidate`
|
||||
callbacks. When a flush occurs, if the computation has been invalidated
|
||||
and not stopped, then the computation is rerun by setting the
|
||||
`invalidated` property to `false` and calling the original function
|
||||
that was passed to `Tracker.autorun`. A flush will occur when the current
|
||||
code finishes running, or sooner if `Tracker.flush` is called.
|
||||
|
||||
Stopping a computation invalidates it (if it is valid) for the purpose
|
||||
of calling callbacks, but ensures that it will never be rerun.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
// If we're in a computation, then perform some clean-up when the current
|
||||
// computation is invalidated (rerun or stopped).
|
||||
if (Tracker.active) {
|
||||
Tracker.onInvalidate(() => {
|
||||
x.destroy();
|
||||
y.finalize();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
<ApiBox name="Tracker.Computation#stop" instanceName="computation"/>
|
||||
|
||||
Stopping a computation is irreversible and guarantees that it will
|
||||
never be rerun. You can stop a computation at any time, including
|
||||
from the computation's own run function. Stopping a computation that
|
||||
is already stopped has no effect.
|
||||
|
||||
Stopping a computation causes its `onInvalidate` callbacks to run
|
||||
immediately if it is not currently invalidated, as well as its
|
||||
`stop` callbacks.
|
||||
|
||||
Nested computations are stopped automatically when their enclosing
|
||||
computation is rerun.
|
||||
|
||||
<ApiBox name="Tracker.Computation#invalidate"instanceName="computation" />
|
||||
|
||||
Invalidating a computation marks it to be rerun at
|
||||
[flush time](#Tracker-flush), at
|
||||
which point the computation becomes valid again. It is rare to
|
||||
invalidate a computation manually, because reactive data sources
|
||||
invalidate their calling computations when they change. Reactive data
|
||||
sources in turn perform this invalidation using one or more
|
||||
[`Tracker.Dependency`](#Tracker-Dependency) objects.
|
||||
|
||||
Invalidating a computation immediately calls all `onInvalidate`
|
||||
callbacks registered on it. Invalidating a computation that is
|
||||
currently invalidated or is stopped has no effect. A computation can
|
||||
invalidate itself, but if it continues to do so indefinitely, the
|
||||
result will be an infinite loop.
|
||||
|
||||
<ApiBox name="Tracker.Computation#onInvalidate" instanceName="computation" />
|
||||
|
||||
`onInvalidate` registers a one-time callback that either fires
|
||||
immediately or as soon as the computation is next invalidated or
|
||||
stopped. It is used by reactive data sources to clean up resources or
|
||||
break dependencies when a computation is rerun or stopped.
|
||||
|
||||
To get a callback after a computation has been recomputed, you can
|
||||
call [`Tracker.afterFlush`](#Tracker-afterFlush) from `onInvalidate`.
|
||||
|
||||
<ApiBox name="Tracker.Computation#onStop" instanceName="computation"/>
|
||||
|
||||
<ApiBox name="Tracker.Computation#stopped" instanceName="computation"/>
|
||||
|
||||
<ApiBox name="Tracker.Computation#invalidated"instanceName="computation"/>
|
||||
|
||||
This property is initially false. It is set to true by `stop()` and
|
||||
`invalidate()`. It is reset to false when the computation is
|
||||
recomputed at flush time.
|
||||
|
||||
<ApiBox name="Tracker.Computation#firstRun" instanceName="computation"/>
|
||||
|
||||
This property is a convenience to support the common pattern where a
|
||||
computation has logic specific to the first run.
|
||||
|
||||
<ApiBox name="Tracker.Computation#firstRunPromise" instanceName="computation" />
|
||||
|
||||
`Computation.firstRunPromise` will be set to the result of the call of the autorun function after the initial computation has been completed. If the autorun function is an async function, it'll then contain its promise, thus making the completion of the execution await-able. That allows us to manually synchronize autoruns like this:
|
||||
|
||||
```js
|
||||
|
||||
await Tracker.autorun(async () => {
|
||||
await Meteor.userAsync();
|
||||
(...more async code...)
|
||||
}).firstRunPromise;
|
||||
|
||||
await Tracker.autorun(async () => {
|
||||
await asyncSomeOrOther();
|
||||
(...more async code...)
|
||||
}).firstRunPromise;
|
||||
|
||||
```
|
||||
|
||||
For a better developer experience `firstRunPromise` is automatically appended to your async `autorun` calls so you don't have to write them yourself. Meaning this also works:
|
||||
|
||||
```js
|
||||
|
||||
await Tracker.autorun(async () => {
|
||||
await Meteor.userAsync();
|
||||
(...more async code...)
|
||||
});
|
||||
|
||||
await Tracker.autorun(async () => {
|
||||
await asyncSomeOrOther();
|
||||
(...more async code...)
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Tracker.Dependency {#Tracker-Dependency}
|
||||
|
||||
A Dependency represents an atomic unit of reactive data that a
|
||||
computation might depend on. Reactive data sources such as Session or
|
||||
Minimongo internally create different Dependency objects for different
|
||||
pieces of data, each of which may be depended on by multiple
|
||||
computations. When the data changes, the computations are
|
||||
invalidated.
|
||||
|
||||
Dependencies don't store data, they just track the set of computations to
|
||||
invalidate if something changes. Typically, a data value will be
|
||||
accompanied by a Dependency object that tracks the computations that depend
|
||||
on it, as in this example:
|
||||
|
||||
```js
|
||||
let weather = 'sunny';
|
||||
const weatherDep = new Tracker.Dependency();
|
||||
|
||||
function getWeather() {
|
||||
weatherDep.depend();
|
||||
return weather;
|
||||
}
|
||||
|
||||
function setWeather(newWeather) {
|
||||
weather = newWeather;
|
||||
|
||||
// Note: We could add logic here to only call `changed` if the new value is
|
||||
// different from the old value.
|
||||
weatherDep.changed();
|
||||
}
|
||||
```
|
||||
|
||||
This example implements a weather data source with a simple getter and
|
||||
setter. The getter records that the current computation depends on
|
||||
the `weatherDep` dependency using `depend()`, while the setter
|
||||
signals the dependency to invalidate all dependent computations by
|
||||
calling `changed()`.
|
||||
|
||||
The reason Dependencies do not store data themselves is that it can be
|
||||
useful to associate multiple Dependencies with the same piece of data.
|
||||
For example, one Dependency might represent the result of a database
|
||||
query, while another might represent just the number of documents in
|
||||
the result. A Dependency could represent whether the weather is sunny
|
||||
or not, or whether the temperature is above freezing.
|
||||
[`Session.equals`](./session.md#Session-equals) is implemented this way for
|
||||
efficiency. When you call `Session.equals('weather', 'sunny')`, the
|
||||
current computation is made to depend on an internal Dependency that
|
||||
does not change if the weather goes from, say, `rainy` to `cloudy`.
|
||||
|
||||
Conceptually, the only two things a Dependency can do are gain a
|
||||
dependent and change.
|
||||
|
||||
A Dependency's dependent computations are always valid (they have
|
||||
`invalidated === false`). If a dependent is invalidated at any time,
|
||||
either by the Dependency itself or some other way, it is immediately
|
||||
removed.
|
||||
|
||||
See the [Tracker manual](https://github.com/meteor/docs/blob/master/long-form/tracker-manual.md#creating-a-reactive-value-using-trackerdependency)
|
||||
to learn how to create a reactive data source using `Tracker.Dependency`.
|
||||
|
||||
<ApiBox name="Tracker.Dependency#changed" instanceName="dependency"/>
|
||||
|
||||
<ApiBox name="Tracker.Dependency#depend" instanceName="dependency"/>
|
||||
|
||||
`dependency.depend()` is used in reactive data source implementations to record
|
||||
the fact that `dependency` is being accessed from the current computation.
|
||||
|
||||
<ApiBox name="Tracker.Dependency#hasDependents" instanceName="dependency"/>
|
||||
|
||||
For reactive data sources that create many internal Dependencies,
|
||||
this function is useful to determine whether a particular Dependency is
|
||||
still tracking any dependency relationships or if it can be cleaned up
|
||||
to save memory.
|
||||
950
v3-docs/docs/api/accounts.md
Normal file
950
v3-docs/docs/api/accounts.md
Normal file
@@ -0,0 +1,950 @@
|
||||
# Accounts
|
||||
|
||||
## Accounts-base {#accounts-base}
|
||||
|
||||
The Meteor Accounts system builds on top of the `userId` support in
|
||||
[`publish`](./meteor#Subscription-userId) and [`methods`](./meteor#methods-userId). The core
|
||||
packages add the concept of user documents stored in the database, and
|
||||
additional packages add [secure password authentication](#passwords),
|
||||
[integration with third party login services](#Meteor-loginWith%3CExternalService%3E),
|
||||
and a [pre-built userinterface](/packages/accounts-ui.html).
|
||||
|
||||
The basic Accounts system is in the `accounts-base` package, but
|
||||
applications typically include this automatically by adding one of the
|
||||
login provider packages: `accounts-password`, `accounts-facebook`,
|
||||
`accounts-github`, `accounts-google`, `accounts-meetup`,
|
||||
`accounts-twitter`, or `accounts-weibo`.
|
||||
|
||||
Read more about customizing user accounts in the [Accounts](http://guide.meteor.com/accounts.html) article in the Meteor Guide.
|
||||
|
||||
<ApiBox name="Meteor.user" hasCustomExample/>
|
||||
|
||||
Retrieves the user record for the current user from
|
||||
the [`Meteor.users`](#Meteor-users) collection.
|
||||
|
||||
On the client, the available fields will be those that
|
||||
are published from the server (other fields won't be available on the
|
||||
client). By default the server publishes `username`, `emails`, and
|
||||
`profile` (writable by user). See [`Meteor.users`](#Meteor-users) for more on
|
||||
the fields used in user documents.
|
||||
|
||||
On the server, this will fetch the record from the database. To improve the
|
||||
latency of a method that uses the user document multiple times, save the
|
||||
returned record to a variable instead of re-calling `Meteor.user()`.
|
||||
|
||||
Fetching the full user document can cause unnecessary database usage on the
|
||||
server and over-reactivity on the client, particularly if you store lots of
|
||||
custom data on it. Therefore it is recommended to use the `options`
|
||||
parameter to only fetch the fields you need:
|
||||
|
||||
```js
|
||||
import { Meteor } from "meteor/meteor";
|
||||
const userName = Meteor.user({ fields: { "profile.name": 1 } }).profile.name;
|
||||
```
|
||||
|
||||
<ApiBox name="Meteor.userAsync" hasCustomExample/>
|
||||
|
||||
Same as [`Meteor.user`](#Meteor-user), but returns a promise and is available on the server.
|
||||
|
||||
```js
|
||||
import { Meteor } from "meteor/meteor";
|
||||
const user = await Meteor.userAsync();
|
||||
```
|
||||
|
||||
<ApiBox name="Meteor.userId" />
|
||||
|
||||
<ApiBox name="Meteor.users" />
|
||||
|
||||
This collection contains one document per registered user. Here's an example
|
||||
user document:
|
||||
|
||||
```js
|
||||
{
|
||||
_id: 'QwkSmTCZiw5KDx3L6', // Meteor.userId()
|
||||
username: 'cool_kid_13', // Unique name
|
||||
emails: [
|
||||
// Each email address can only belong to one user.
|
||||
{ address: 'cool@example.com', verified: true },
|
||||
{ address: 'another@different.com', verified: false }
|
||||
],
|
||||
createdAt: new Date('Wed Aug 21 2013 15:16:52 GMT-0700 (PDT)'),
|
||||
profile: {
|
||||
// The profile is writable by the user by default.
|
||||
name: 'Joe Schmoe'
|
||||
},
|
||||
services: {
|
||||
facebook: {
|
||||
id: '709050', // Facebook ID
|
||||
accessToken: 'AAACCgdX7G2...AbV9AZDZD'
|
||||
},
|
||||
resume: {
|
||||
loginTokens: [
|
||||
{ token: '97e8c205-c7e4-47c9-9bea-8e2ccc0694cd',
|
||||
when: 1349761684048 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A user document can contain any data you want to store about a user. Meteor
|
||||
treats the following fields specially:
|
||||
|
||||
- `username`: a unique String identifying the user.
|
||||
- `emails`: an Array of Objects with keys `address` and `verified`;
|
||||
an email address may belong to at most one user. `verified` is
|
||||
a Boolean which is true if the user has [verified the address](#Accounts-verifyEmail) with a token sent over email.
|
||||
- `createdAt`: the Date at which the user document was created.
|
||||
- `profile`: an Object which the user can create and update with any data.
|
||||
Do not store anything on `profile` that you wouldn't want the user to edit
|
||||
unless you have a deny rule on the `Meteor.users` collection.
|
||||
- `services`: an Object containing data used by particular
|
||||
login services. For example, its `reset` field contains
|
||||
tokens used by [forgot password](#Accounts-forgotPassword) links,
|
||||
and its `resume` field contains tokens used to keep you
|
||||
logged in between sessions.
|
||||
|
||||
Like all [Mongo.Collection](./collections.md)s, you can access all
|
||||
documents on the server, but only those specifically published by the server are
|
||||
available on the client. You can also use all Collection methods, for instance
|
||||
`Meteor.users.remove` on the server to delete a user.
|
||||
|
||||
By default, the current user's `username`, `emails` and `profile` are
|
||||
published to the client. You can publish additional fields for the
|
||||
current user with:
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [server.js]
|
||||
Meteor.publish("userData", function () {
|
||||
if (this.userId) {
|
||||
return Meteor.users.find(
|
||||
{ _id: this.userId },
|
||||
{
|
||||
fields: { other: 1, things: 1 },
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.ready();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```js [client.js]
|
||||
Meteor.subscribe("userData");
|
||||
```
|
||||
|
||||
:::
|
||||
If the autopublish package is installed, information about all users
|
||||
on the system is published to all clients. This includes `username`,
|
||||
`profile`, and any fields in `services` that are meant to be public
|
||||
(eg `services.facebook.id`,
|
||||
`services.twitter.screenName`). Additionally, when using autopublish
|
||||
more information is published for the currently logged in user,
|
||||
including access tokens. This allows making API calls directly from
|
||||
the client for services that allow this.
|
||||
|
||||
Users are by default allowed to specify their own `profile` field with
|
||||
[`Accounts.createUser`](#Accounts-createUser) and modify it with
|
||||
`Meteor.users.update`. To allow users to edit additional fields, use
|
||||
[`Meteor.users.allow`](./collections.md#Mongo-Collection-allow). To forbid users from making any modifications to
|
||||
their user document:
|
||||
|
||||
```js
|
||||
import { Meteor } from "meteor/meteor";
|
||||
Meteor.users.deny({ update: () => true });
|
||||
```
|
||||
|
||||
<ApiBox name="Meteor.loggingIn" />
|
||||
|
||||
For example, [the `accounts-ui` package](../packages/accounts-ui.md) uses this to display an
|
||||
animation while the login request is being processed.
|
||||
|
||||
<ApiBox name="Meteor.loggingOut" />
|
||||
|
||||
<ApiBox name="Meteor.logout" />
|
||||
|
||||
<ApiBox name="Meteor.logoutOtherClients" />
|
||||
|
||||
For example, when called in a user's browser, connections in that browser
|
||||
remain logged in, but any other browsers or DDP clients logged in as that user
|
||||
will be logged out.
|
||||
|
||||
<ApiBox name="Meteor.loginWithPassword" />
|
||||
|
||||
If there are multiple users with a username or email only differing in case, a case sensitive match is required. Although `createUser` won't let you create users with ambiguous usernames or emails, this could happen with existing databases or if you modify the users collection directly.
|
||||
|
||||
This method can fail throwing one of the following errors:
|
||||
|
||||
- "Unrecognized options for login request [400]" if `user` or `password` is undefined.
|
||||
- "Match failed [400]" if `user` isn't an Object or String, or `password` isn't a String.
|
||||
- "User not found [403]" if the email or username provided in `user` doesn't belong to a registered user.
|
||||
- "Incorrect password [403]" if the password provided is incorrect.
|
||||
- "User has no password set [403]" if `user` doesn't have a password.
|
||||
|
||||
This function is provided by the `accounts-password` package. See the
|
||||
[Passwords](#passwords) section below.
|
||||
|
||||
<ApiBox name="Meteor.loginWith<ExternalService>" />
|
||||
|
||||
Available functions are:
|
||||
|
||||
- `Meteor.loginWithMeteorDeveloperAccount`
|
||||
- `Meteor.loginWithFacebook`
|
||||
- `options` may also include [Facebook's `auth_type` parameter](https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#reaskperms)
|
||||
- `Meteor.loginWithGithub`
|
||||
- `Meteor.loginWithGoogle`
|
||||
- `options` may also include [Google's additional URI parameters](https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters)
|
||||
- `Meteor.loginWithMeetup`
|
||||
- `Meteor.loginWithTwitter`
|
||||
- `options` may also include [Twitter's `force_login` parameter](https://dev.twitter.com/oauth/reference/get/oauth/authenticate)
|
||||
- `Meteor.loginWithWeibo`
|
||||
|
||||
These functions initiate the login process with an external
|
||||
service (eg: Facebook, Google, etc), using OAuth. When called they open a new pop-up
|
||||
window that loads the provider's login page. Once the user has logged in
|
||||
with the provider, the pop-up window is closed and the Meteor client
|
||||
logs in to the Meteor server with the information provided by the external
|
||||
service.
|
||||
|
||||
<h3 id="requestpermissions" name="requestpermissions">Requesting Permissions</h3>
|
||||
|
||||
In addition to identifying the user to your application, some services
|
||||
have APIs that allow you to take action on behalf of the user. To
|
||||
request specific permissions from the user, pass the
|
||||
`requestPermissions` option the login function. This will cause the user
|
||||
to be presented with an additional page in the pop-up dialog to permit
|
||||
access to their data. The user's `accessToken` — with permissions
|
||||
to access the service's API — is stored in the `services` field of
|
||||
the user document. The supported values for `requestPermissions` differ
|
||||
for each login service and are documented on their respective developer
|
||||
sites:
|
||||
|
||||
- Facebook: <http://developers.facebook.com/docs/authentication/permissions/>
|
||||
- GitHub: <http://developer.github.com/v3/oauth/#scopes>
|
||||
- Google: <https://developers.google.com/identity/protocols/googlescopes>
|
||||
- Meetup: <http://www.meetup.com/meetup_api/auth/#oauth2-scopes>
|
||||
- Twitter, Weibo, Meteor developer accounts: `requestPermissions` currently not supported
|
||||
|
||||
External login services typically require registering and configuring
|
||||
your application before use. The easiest way to do this is with the
|
||||
[`accounts-ui` package](../packages/accounts-ui.md) which presents a step-by-step guide
|
||||
to configuring each service. However, the data can be also be entered
|
||||
manually in the `ServiceConfiguration.configurations` collection, which
|
||||
is exported by the `service-configuration` package.
|
||||
|
||||
## Configuring Services {#service-configuration}
|
||||
|
||||
First, add the service configuration package:
|
||||
|
||||
```bash
|
||||
meteor add service-configuration
|
||||
```
|
||||
|
||||
Then, inside the server of your app (this example is for the Weebo service), import `ServiceConfiguration`:
|
||||
|
||||
```js
|
||||
import { ServiceConfiguration } from "meteor/service-configuration";
|
||||
ServiceConfiguration.configurations.upsert(
|
||||
{ service: "weibo" },
|
||||
{
|
||||
$set: {
|
||||
loginStyle: "popup",
|
||||
clientId: "1292962797", // See table below for correct property name!
|
||||
secret: "75a730b58f5691de5522789070c319bc",
|
||||
},
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Since Meteor 2.7 you no longer need to manually set the configuration and instead can use Meteor settings by setting your services under `Meteor.settings.packages.service-configuration.<service>`. All the properties can be set under the service and will be added to the database as is, so make sure that they are correct. For the example above, the settings would look like:
|
||||
|
||||
```json
|
||||
{
|
||||
"packages": {
|
||||
"service-configuration": {
|
||||
"weibo": {
|
||||
"loginStyle": "popup",
|
||||
"clientId": "1292962797",
|
||||
"secret": "75a730b58f5691de5522789070c319bc"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The correct property name to use for the API identifier (i.e. `clientId` in the above example) depends on the login service being used, so be sure to use the correct one:
|
||||
|
||||
| Property Name | Services |
|
||||
| ------------- | -------------------------------------------------------- |
|
||||
| `appId` | Facebook |
|
||||
| `clientId` | Github, Google, Meetup, Meteor Developer Accounts, Weibo |
|
||||
| `consumerKey` | Twitter |
|
||||
|
||||
Additionally, each external service has its own login provider package and login function. For
|
||||
example, to support GitHub login, run the following in your terminal:
|
||||
|
||||
```bash
|
||||
meteor add accounts-github
|
||||
```
|
||||
|
||||
and use the `Meteor.loginWithGithub` function:
|
||||
|
||||
```js
|
||||
import { Meteor } from "meteor/meteor";
|
||||
Meteor.loginWithGithub(
|
||||
{
|
||||
requestPermissions: ["user", "public_repo"],
|
||||
},
|
||||
(error) => {
|
||||
if (error) {
|
||||
Session.set("errorMessage", error.reason || "Unknown error");
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Login service configuration is sent from the server to the client over DDP when
|
||||
your app starts up; you may not call the login function until the configuration
|
||||
is loaded. The function `Accounts.loginServicesConfigured()` is a reactive data
|
||||
source that will return true once the login service is configured; you should
|
||||
not make login buttons visible or active until it is true.
|
||||
|
||||
Ensure that your [`$ROOT_URL`](./meteor.md#Meteor-absoluteUrl) matches the authorized
|
||||
domain and callback URL that you configure with the external service (for
|
||||
instance, if you are running Meteor behind a proxy server, `$ROOT_URL` should be
|
||||
the externally-accessible URL, not the URL inside your proxy).
|
||||
|
||||
## Manual service configuration {#manual-service-configuration}
|
||||
|
||||
You can use `Accounts.loginServiceConfiguration` to view and edit the settings collection:
|
||||
|
||||
```js
|
||||
import { Accounts } from "meteor/accounts-base";
|
||||
Accounts.loginServiceConfiguration.find();
|
||||
```
|
||||
|
||||
## Popup versus redirect flow {#popup-vs-redirect-flow}
|
||||
|
||||
When configuring OAuth login with a provider (such as Facebook or Google), Meteor lets you choose a popup- or redirect-based flow. In a popup-based flow, when a user logs in, they will be prompted to login at the provider in a popup window. In a redirect-based flow, the user's whole browser window will be redirected to the login provider, and the window will redirect back to your app when the login is completed.
|
||||
|
||||
You can also pick which type of login to do by passing an option to [`Meteor.loginWith<ExternalService>`](#Meteor-loginWith%3CExternalService%3E)
|
||||
|
||||
Usually, the popup-based flow is preferable because the user will not have to reload your whole app at the end of the login flow. However, the popup-based flow requires browser features such as `window.close` and `window.opener` that are not available in all mobile environments. In particular, we recommend using `Meteor.loginWith<ExternalService>({ loginStyle: 'redirect' })` in the following environments:
|
||||
|
||||
- Inside UIWebViews (when your app is loaded inside a mobile app)
|
||||
- In Safari on iOS8 (`window.close` is not supported due to a bug)
|
||||
|
||||
<ApiBox name="currentUser" />
|
||||
|
||||
<ApiBox name="loggingIn" />
|
||||
|
||||
<ApiBox name="Accounts.ui.config" hasCustomExample/>
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
import { Accounts } from "meteor/accounts-base";
|
||||
|
||||
Accounts.ui.config({
|
||||
requestPermissions: {
|
||||
facebook: ["user_likes"],
|
||||
github: ["user", "repo"],
|
||||
},
|
||||
requestOfflineToken: {
|
||||
google: true,
|
||||
},
|
||||
passwordSignupFields: "USERNAME_AND_OPTIONAL_EMAIL",
|
||||
});
|
||||
```
|
||||
|
||||
Since Meteor 2.7 you can configure these in your Meteor settings under `Meteor.settings.public.packages.accounts-ui-unstyled`.
|
||||
|
||||
## Multi-server {#multi-server}
|
||||
|
||||
The `accounts-base` package exports two constructors, called
|
||||
`AccountsClient` and `AccountsServer`, which are used to create the
|
||||
`Accounts` object that is available on the client and the server,
|
||||
respectively.
|
||||
|
||||
This predefined `Accounts` object (along with similar convenience methods
|
||||
of `Meteor`, such as [`Meteor.logout`](#Meteor-logout)) is sufficient to
|
||||
implement most accounts-related logic in Meteor apps. Nevertheless, these
|
||||
two constructors can be instantiated more than once, to create multiple
|
||||
independent connections between different accounts servers and their
|
||||
clients, in more complicated authentication situations.
|
||||
|
||||
<ApiBox name="AccountsCommon"/>
|
||||
|
||||
The `AccountsClient` and `AccountsServer` classes share a common
|
||||
superclass, `AccountsCommon`. Methods defined on
|
||||
`AccountsCommon.prototype` will be available on both the client and the
|
||||
server, via the predefined `Accounts` object (most common) or any custom
|
||||
`accountsClientOrServer` object created using the `AccountsClient` or
|
||||
`AccountsServer` constructors (less common).
|
||||
|
||||
Here are a few of those methods:
|
||||
|
||||
<ApiBox name="AccountsCommon#userId" instanceName="accountsCommon"/>
|
||||
|
||||
<ApiBox name="AccountsCommon#user" instanceName="accountsCommon"/>
|
||||
|
||||
<ApiBox name="AccountsCommon#config" instanceName="accountsCommon"/>
|
||||
|
||||
From Meteor 2.5 you can set these in your Meteor settings under `Meteor.settings.packages.accounts-base`. Note that due to the nature of settings file you won't be able to set parameters that require functions.
|
||||
|
||||
<ApiBox name="AccountsCommon#onLogin" instanceName="accountsCommon"/>
|
||||
|
||||
See description of [AccountsCommon#onLoginFailure](#AccountsCommon-onLoginFailure)
|
||||
for details.
|
||||
|
||||
<ApiBox name="AccountsCommon#onLoginFailure" instanceName="accountsCommon"/>
|
||||
|
||||
Either the `onLogin` or the `onLoginFailure` callbacks will be called
|
||||
for each login attempt. The `onLogin` callbacks are called after the
|
||||
user has been successfully logged in. The `onLoginFailure` callbacks are
|
||||
called after a login attempt is denied.
|
||||
|
||||
These functions return an object with a single method, `stop`. Calling
|
||||
`stop()` unregisters the callback.
|
||||
|
||||
On the server, the callbacks get a single argument, the same attempt info
|
||||
object as [`validateLoginAttempt`](#AccountsServer-validateLoginAttempt). On the
|
||||
client, the callback argument is an object containing a single `error`
|
||||
property set to the `Error`-object which was received from the failed login
|
||||
attempt.
|
||||
|
||||
<ApiBox name="AccountsCommon#onLogout" instanceName="accountsCommon" hasCustomExample/>
|
||||
|
||||
On the server, the `func` callback receives a single argument with the object below. On the
|
||||
client, no arguments are passed.
|
||||
|
||||
```js
|
||||
import { AccountsCommon } from "meteor/accounts-base";
|
||||
const options = {
|
||||
//...
|
||||
};
|
||||
const accountsCommon = new AccountsCommon(options);
|
||||
|
||||
accountsCommon.onLogout(({ user, connection, collection }) => {
|
||||
console.log(user);
|
||||
// ˆˆˆˆˆˆ The Meteor user object of the user which just logged out
|
||||
console.log(connection);
|
||||
// ˆˆˆˆˆˆ The connection object the request came in on. See
|
||||
// `Meteor.onConnection` for details.
|
||||
|
||||
console.log(collection);
|
||||
// ˆˆˆˆˆˆ The `collection` The name of the Mongo.Collection or the
|
||||
// Mongo.Collection object to hold the users.
|
||||
});
|
||||
```
|
||||
|
||||
<ApiBox name="AccountsClient"/>
|
||||
|
||||
At most one of `options.connection` and `options.ddpUrl` should be
|
||||
provided in any instantiation of `AccountsClient`. If neither is provided,
|
||||
`Meteor.connection` will be used as the `.connection` property of the
|
||||
`AccountsClient` instance.
|
||||
|
||||
Note that `AccountsClient` is currently available only on the client, due
|
||||
to its use of browser APIs such as `window.localStorage`. In principle,
|
||||
though, it might make sense to establish a client connection from one
|
||||
server to another remote accounts server. Please [let us
|
||||
know](https://github.com/meteor/meteor/wiki/Contributing-to-Meteor#feature-requests)
|
||||
if you find yourself needing this server-to-server functionality.
|
||||
|
||||
These methods are defined on `AccountsClient.prototype`, and are thus
|
||||
available only on the client:
|
||||
|
||||
<ApiBox name="AccountsClient#loggingIn" instanceName="accountsClient"/>
|
||||
|
||||
<ApiBox name="AccountsClient#logout" instanceName="accountsClient"/>
|
||||
|
||||
<ApiBox name="AccountsClient#logoutOtherClients" instanceName="accountsClient"/>
|
||||
|
||||
<ApiBox name="AccountsServer"/>
|
||||
|
||||
These methods are defined on `AccountsServer.prototype`, and are thus
|
||||
available only on the server:
|
||||
|
||||
<ApiBox name="AccountsServer#validateNewUser" instanceName="accountsServer"/>
|
||||
|
||||
This can be called multiple times. If any of the functions return `false` or
|
||||
throw an error, the new user creation is aborted. To set a specific error
|
||||
message (which will be displayed by [`accounts-ui`](../packages/accounts-ui.md)), throw a new
|
||||
[`Meteor.Error`](./meteor#meteor-api).
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
import { Accounts } from "meteor/accounts-base";
|
||||
|
||||
// Validate username, sending a specific error message on failure.
|
||||
Accounts.validateNewUser((user) => {
|
||||
if (user.username && user.username.length >= 3) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Meteor.Error(403, "Username must have at least 3 characters");
|
||||
}
|
||||
});
|
||||
|
||||
// Validate username, without a specific error message.
|
||||
Accounts.validateNewUser((user) => {
|
||||
return user.username !== "root";
|
||||
});
|
||||
```
|
||||
|
||||
If the user is being created as part of a login attempt from a client (eg,
|
||||
calling [`Accounts.createUser`](#Accounts-createUser) from the client, or
|
||||
[logging in for the first time with an external
|
||||
service](#meteor_loginwithexternalservice)), these callbacks are called _before_
|
||||
the [`Accounts.validateLoginAttempt`](#Accounts-validateLoginAttempt)
|
||||
callbacks. If these callbacks succeed but those fail, the user will still be
|
||||
created but the connection will not be logged in as that user.
|
||||
|
||||
<ApiBox name="AccountsServer#onCreateUser" instanceName="accountsServer" hasCustomExample/>
|
||||
|
||||
Use this when you need to do more than simply accept or reject new user
|
||||
creation. With this function you can programatically control the
|
||||
contents of new user documents.
|
||||
|
||||
The function you pass will be called with two arguments: `options` and
|
||||
`user`. The `options` argument comes
|
||||
from [`Accounts.createUser`](#Accounts-createUser) for
|
||||
password-based users or from an external service login flow. `options` may come
|
||||
from an untrusted client so make sure to validate any values you read from
|
||||
it. The `user` argument is created on the server and contains a
|
||||
proposed user object with all the automatically generated fields
|
||||
required for the user to log in, including the `_id`.
|
||||
|
||||
The function should return the user document (either the one passed in or a
|
||||
newly-created object) with whatever modifications are desired. The returned
|
||||
document is inserted directly into the [`Meteor.users`](#Meteor-users) collection.
|
||||
|
||||
The default create user function simply copies `options.profile` into
|
||||
the new user document. Calling `onCreateUser` overrides the default
|
||||
hook. This can only be called once.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
import { Accounts } from "meteor/accounts-base";
|
||||
// Support for playing D&D: Roll 3d6 for dexterity.
|
||||
Accounts.onCreateUser((options, user) => {
|
||||
const customizedUser = Object.assign(
|
||||
{
|
||||
dexterity: _.random(1, 6) + _.random(1, 6) + _.random(1, 6),
|
||||
},
|
||||
user
|
||||
);
|
||||
|
||||
// We still want the default hook's 'profile' behavior.
|
||||
if (options.profile) {
|
||||
customizedUser.profile = options.profile;
|
||||
}
|
||||
|
||||
return customizedUser;
|
||||
});
|
||||
```
|
||||
|
||||
<ApiBox name="AccountsServer#validateLoginAttempt" instanceName="accountsServer" hasCustomExample/>
|
||||
|
||||
Call `validateLoginAttempt` with a callback to be called on login
|
||||
attempts. It returns an object with a single method, `stop`. Calling
|
||||
`stop()` unregisters the callback.
|
||||
|
||||
When a login attempt is made, the registered validate login callbacks
|
||||
are called with a single argument, you can check the example:
|
||||
|
||||
```js
|
||||
import { AccountsServer } from "meteor/accounts-base";
|
||||
const options = {
|
||||
//...
|
||||
};
|
||||
const accountsServer = new AccountsServer(options);
|
||||
|
||||
accountsServer.validateLoginAttempt(
|
||||
({
|
||||
type, // String
|
||||
allowed, // Boolean
|
||||
error, // Error
|
||||
user, // Object
|
||||
connection, // Object
|
||||
collection, // Object
|
||||
methodName, // String
|
||||
methodArguments, // Array<String>
|
||||
}) => {
|
||||
console.log(type);
|
||||
// ˆˆˆˆˆˆ The service name, such as "password" or "twitter".
|
||||
|
||||
console.log(allowed);
|
||||
// ˆˆˆˆˆˆ Whether this login is allowed and will be successful (if not aborted
|
||||
// by any of the validateLoginAttempt callbacks). False if the login
|
||||
// will not succeed (for example, an invalid password or the login was
|
||||
// aborted by a previous validateLoginAttempt callback).
|
||||
|
||||
console.log(error);
|
||||
// ˆˆˆˆˆˆ When `allowed` is false, the exception describing why the login
|
||||
// failed. It will be a `Meteor.Error` for failures reported to the
|
||||
// user (such as invalid password), and can be a another kind of
|
||||
// exception for internal errors.
|
||||
|
||||
console.log(user);
|
||||
// ˆˆˆˆˆˆ When it is known which user was attempting to login,
|
||||
// the Meteor user object. This will always be present for successful logins.
|
||||
|
||||
console.log(connection);
|
||||
// ˆˆˆˆˆˆ The `connection` object the request came in on. See
|
||||
// [`Meteor.onConnection`](#meteor_onconnection) for details.
|
||||
|
||||
console.log(collection);
|
||||
// ˆˆˆˆˆˆ The `collection` The name of the Mongo.Collection or the
|
||||
// Mongo.Collection object to hold the users.
|
||||
|
||||
console.log(methodName);
|
||||
// ˆˆˆˆˆˆ The name of the Meteor method being used to login.
|
||||
// For example, "login", "loginWithPassword", or "loginWith<ExternalService>".
|
||||
|
||||
console.log(methodArguments);
|
||||
// ˆˆˆˆˆˆ An array of the arguments passed to the login method.
|
||||
// For example, `["username", "password"]`
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
A validate login callback must return a truthy value for the login to
|
||||
proceed. If the callback returns a falsy value or throws an
|
||||
exception, the login is aborted. Throwing a `Meteor.Error` will
|
||||
report the error reason to the user.
|
||||
|
||||
All registered validate login callbacks are called, even if one of the callbacks
|
||||
aborts the login. The later callbacks will see the `allowed` field set to
|
||||
`false` since the login will now not be successful. This allows later callbacks
|
||||
to override an error from a previous callback; for example, you could override
|
||||
the "Incorrect password" error with a different message.
|
||||
|
||||
Validate login callbacks that aren't explicitly trying to override a previous
|
||||
error generally have no need to run if the attempt has already been determined
|
||||
to fail, and should start with
|
||||
|
||||
```js
|
||||
if (!attempt.allowed) {
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
<ApiBox name="AccountsServer#beforeExternalLogin" instanceName="accountsServer" hasCustomExample/>
|
||||
|
||||
Use this hook if you need to validate that user from an external service should
|
||||
be allowed to login or create account.
|
||||
|
||||
```js
|
||||
import { AccountsServer } from "meteor/accounts-base";
|
||||
const options = {
|
||||
//...
|
||||
};
|
||||
const accountsServer = new AccountsServer(options);
|
||||
|
||||
accountsServer.beforeExternalLogin(({ type, data, user }) => {
|
||||
console.log(type);
|
||||
// ˆˆˆˆˆˆ The service name, such as "google" or "twitter". Is a String
|
||||
|
||||
console.log(data);
|
||||
// ˆˆˆˆˆˆ Data retrieved from the service (eg: email, name, etc)
|
||||
// Is an Object.
|
||||
|
||||
console.log(user);
|
||||
// ˆˆˆˆˆˆ If user was found in the database that matches the criteria from the service,
|
||||
// their data will be provided here. Is an Object.
|
||||
});
|
||||
```
|
||||
|
||||
You should return a `Boolean` value, `true` if the login/registration should
|
||||
proceed or `false` if it should terminate. In case of termination
|
||||
the login attempt will throw an error `403`, with the message: `Login forbidden`.
|
||||
|
||||
<ApiBox name="AccountsServer#setAdditionalFindUserOnExternalLogin" hasCustomExample instanceName="accountsServer"/>
|
||||
|
||||
When allowing your users to authenticate with an external service, the process will
|
||||
eventually call `Accounts.updateOrCreateUserFromExternalService`. By default, this
|
||||
will search for a user with the `service.<servicename>.id`, and if not found will
|
||||
create a new user. As that is not always desirable, you can use this hook as an
|
||||
escape hatch to look up a user with a different selector, probably by `emails.address` or `username`. Note the function will only be called if no user was found with the
|
||||
`service.<servicename>.id` selector.
|
||||
|
||||
The function will be called with a single argument, the info object:
|
||||
|
||||
```js
|
||||
import { AccountsServer } from "meteor/accounts-base";
|
||||
const options = {
|
||||
//...
|
||||
};
|
||||
const accountsServer = new AccountsServer(options);
|
||||
|
||||
accountsServer.setAdditionalFindUserOnExternalLogin(
|
||||
({ serviceName, serviceData, options }) => {
|
||||
// serviceName: String
|
||||
// The external service name, such as "google" or "twitter".
|
||||
// serviceData: Object
|
||||
// The data returned by the service oauth request.
|
||||
// options: Object
|
||||
// An optional arugment passed down from the oauth service that may contain
|
||||
// additional user profile information. As the data in `options` comes from an
|
||||
// external source, make sure you validate any values you read from it.
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
The function should return either a user document or `undefined`. Returning a user
|
||||
will result in the populating the `service.<servicename>` in your user document,
|
||||
while returning `undefined` will result in a new user account being created.
|
||||
If you would prefer that a new account not be created, you could throw an error
|
||||
instead of returning.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
// If a user has already been created, and used their Google email, this will
|
||||
// allow them to sign in with the Meteor.loginWithGoogle method later, without
|
||||
// creating a new user.
|
||||
Accounts.setAdditionalFindUserOnExternalLogin(
|
||||
({ serviceName, serviceData }) => {
|
||||
if (serviceName === "google") {
|
||||
// Note: Consider security implications. If someone other than the owner
|
||||
// gains access to the account on the third-party service they could use
|
||||
// the e-mail set there to access the account on your app.
|
||||
// Most often this is not an issue, but as a developer you should be aware
|
||||
// of how bad actors could play.
|
||||
return Accounts.findUserByEmail(serviceData.email);
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
<ApiBox name="AccountsServer#registerLoginHandler" instanceName="accountsServer"/>
|
||||
|
||||
Use this to register your own custom authentication method. This is also used by all of the other inbuilt accounts packages to integrate with the accounts system.
|
||||
|
||||
There can be multiple login handlers that are registered. When a login request is made, it will go through all these handlers to find its own handler.
|
||||
|
||||
The registered handler callback is called with a single argument, the `options` object which comes from the login method. For example, if you want to login with a plaintext password, `options` could be `{ user: { username: <username> }, password: <password> }`,or `{ user: { email: <email> }, password: <password> }`.
|
||||
|
||||
The login handler should return `undefined` if it's not going to handle the login request or else the login result object.
|
||||
|
||||
<h2 id="accounts_rate_limit">Rate Limiting</h2>
|
||||
|
||||
By default, there are rules added to the [`DDPRateLimiter`](./DDPRateLimiter.md)
|
||||
that rate limit logins, new user registration and password reset calls to a
|
||||
limit of 5 requests per 10 seconds per session. These are a basic solution
|
||||
to dictionary attacks where a malicious user attempts to guess the passwords
|
||||
of legitimate users by attempting all possible passwords.
|
||||
|
||||
These rate limiting rules can be removed by calling
|
||||
`Accounts.removeDefaultRateLimit()`. Please see the
|
||||
[`DDPRateLimiter`](./DDPRateLimiter.md) docs for more information.
|
||||
|
||||
<ApiBox name="AccountsServer#addDefaultRateLimit" instanceName="accountsServer"/>
|
||||
|
||||
<ApiBox name="AccountsServer#removeDefaultRateLimit" instanceName="accountsServer"/>
|
||||
|
||||
## Passwords {#passwords}
|
||||
|
||||
The `accounts-password` package contains a full system for password-based
|
||||
authentication. In addition to the basic username and password-based
|
||||
sign-in process, it also supports email-based sign-in including
|
||||
address verification and password recovery emails.
|
||||
|
||||
The Meteor server stores passwords using the
|
||||
[bcrypt](http://en.wikipedia.org/wiki/Bcrypt) algorithm. This helps
|
||||
protect against embarrassing password leaks if the server's database is
|
||||
compromised.
|
||||
|
||||
To add password support to your application, run this command in your terminal:
|
||||
|
||||
```bash
|
||||
meteor add accounts-password
|
||||
```
|
||||
|
||||
> In addition to configuring the [`email`](./email.md) 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!
|
||||
|
||||
You can construct your own user interface using the
|
||||
functions below, or use the [`accounts-ui` package](../packages/accounts-ui.md) to
|
||||
include a turn-key user interface for password-based sign-in.
|
||||
|
||||
<ApiBox name="Accounts.createUser" />
|
||||
|
||||
On the client, this function logs in as the newly created user on
|
||||
successful completion. On the server, it returns the newly created user
|
||||
id.
|
||||
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
By default the `profile` option is added directly to the new user document. To
|
||||
override this behavior, use [`Accounts.onCreateUser`](#Accounts-onCreateUser).
|
||||
|
||||
This function is only used for creating users with passwords. The external
|
||||
service login flows do not use this function.
|
||||
|
||||
Instead of modifying documents in the [`Meteor.users`](#Meteor-users) collection
|
||||
directly, use these convenience functions which correctly check for case
|
||||
insensitive duplicates before updates.
|
||||
|
||||
<ApiBox name="Accounts.createUserAsync" />
|
||||
|
||||
<ApiBox name="Accounts.createUserVerifyingEmail" />
|
||||
|
||||
<ApiBox name="Accounts.setUsername" />
|
||||
|
||||
<ApiBox name="Accounts.addEmail" />
|
||||
|
||||
By default, an email address is added with `{ verified: false }`. Use
|
||||
[`Accounts.sendVerificationEmail`](#Accounts-sendVerificationEmail) to send an
|
||||
email with a link the user can use to verify their email address.
|
||||
|
||||
<ApiBox name="Accounts.removeEmail" />
|
||||
|
||||
<ApiBox name="Accounts.verifyEmail" />
|
||||
|
||||
If the user trying to verify the email has 2FA enabled, this error will be thrown:
|
||||
|
||||
- "Email verified, but user not logged in because 2FA is enabled [2fa-enabled]": No longer signing in the user automatically if the user has 2FA enabled.
|
||||
|
||||
This function accepts tokens passed into the callback registered with
|
||||
[`Accounts.onEmailVerificationLink`](#Accounts-onEmailVerificationLink).
|
||||
|
||||
<ApiBox name="Accounts.findUserByUsername" />
|
||||
|
||||
<ApiBox name="Accounts.findUserByEmail" />
|
||||
|
||||
Use the below functions to initiate password changes or resets from the server
|
||||
or the client.
|
||||
|
||||
<ApiBox name="Accounts.changePassword" />
|
||||
|
||||
<ApiBox name="Accounts.forgotPassword" />
|
||||
|
||||
This triggers a call
|
||||
to [`Accounts.sendResetPasswordEmail`](#Accounts-sendResetPasswordEmail)
|
||||
on the server. When the user visits the link in this email, the callback
|
||||
registered with [`Accounts.onResetPasswordLink`](#Accounts-onResetPasswordLink)
|
||||
will be called.
|
||||
|
||||
If you are using the [`accounts-ui` package](../packages/accounts-ui.md), this is handled
|
||||
automatically. Otherwise, it is your responsibility to prompt the user for the
|
||||
new password and call `resetPassword`.
|
||||
|
||||
<ApiBox name="Accounts.resetPassword" />
|
||||
|
||||
This function accepts tokens passed into the callbacks registered with
|
||||
[`AccountsClient#onResetPasswordLink`](#Accounts-onResetPasswordLink) and
|
||||
[`Accounts.onEnrollmentLink`](#Accounts-onEnrollmentLink).
|
||||
|
||||
If the user trying to reset the password has 2FA enabled, this error will be thrown:
|
||||
|
||||
- "Changed password, but user not logged in because 2FA is enabled [2fa-enabled]": No longer signing in the user automatically if the user has 2FA enabled.
|
||||
|
||||
<ApiBox name="Accounts.setPassword" />
|
||||
|
||||
<ApiBox name="Accounts.sendResetPasswordEmail" />
|
||||
|
||||
When the user visits the link in this email, the callback registered with
|
||||
[`AccountsClient#onResetPasswordLink`](#Accounts-onResetPasswordLink) will be called.
|
||||
|
||||
To customize the contents of the email, see
|
||||
[`Accounts.emailTemplates`](#Accounts-emailTemplates).
|
||||
|
||||
<ApiBox name="Accounts.sendEnrollmentEmail" />
|
||||
|
||||
When the user visits the link in this email, the callback registered with
|
||||
[`Accounts.onEnrollmentLink`](#Accounts-onEnrollmentLink) will be called.
|
||||
|
||||
To customize the contents of the email, see
|
||||
[`Accounts.emailTemplates`](#Accounts-emailTemplates).
|
||||
|
||||
<ApiBox name="Accounts.sendVerificationEmail" />
|
||||
|
||||
When the user visits the link in this email, the callback registered with
|
||||
[`Accounts.onEmailVerificationLink`](#Accounts-onEmailVerificationLink) will
|
||||
be called.
|
||||
|
||||
To customize the contents of the email, see
|
||||
[`Accounts.emailTemplates`](#Accounts-emailTemplates).
|
||||
|
||||
<ApiBox name="Accounts.onResetPasswordLink" />
|
||||
|
||||
<ApiBox name="Accounts.onEnrollmentLink" />
|
||||
|
||||
<ApiBox name="Accounts.onEmailVerificationLink" />
|
||||
|
||||
<ApiBox name="Accounts.emailTemplates" />
|
||||
|
||||
This is an `Object` with several fields that are used to generate text/html
|
||||
for the emails sent by `sendResetPasswordEmail`, `sendEnrollmentEmail`,
|
||||
and `sendVerificationEmail`.
|
||||
|
||||
Set the fields of the object by assigning to them:
|
||||
|
||||
- `from`: (**required**) A `String` with an [RFC5322](http://tools.ietf.org/html/rfc5322) From
|
||||
address. By default, the email is sent from `no-reply@example.com`. **If you
|
||||
want e-mails to send correctly, this should be changed to your own domain
|
||||
as most e-mail providers will reject mail sent from `example.com`.**
|
||||
- `siteName`: The public name of your application. Defaults to the DNS name of
|
||||
the application (eg: `awesome.meteor.com`).
|
||||
- `headers`: An `Object` for custom email headers as described in
|
||||
[`Email.send`](./email.md#Email-send).
|
||||
- `resetPassword`: An `Object` with the fields:
|
||||
- `from`: A `Function` used to override the `from` address defined
|
||||
by the `emailTemplates.from` field.
|
||||
- `subject`: A `Function` that takes a user object and returns
|
||||
a `String` for the subject line of a reset password email.
|
||||
- `text`: An optional `Function` that takes a user object and a url, and
|
||||
returns the body text for a reset password email.
|
||||
- `html`: An optional `Function` that takes a user object and a
|
||||
url, and returns the body html for a reset password email.
|
||||
- `enrollAccount`: Same as `resetPassword`, but for initial password setup for
|
||||
new accounts.
|
||||
- `verifyEmail`: Same as `resetPassword`, but for verifying the users email
|
||||
address.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
import { Accounts } from "meteor/accounts-base";
|
||||
|
||||
Accounts.emailTemplates.siteName = "AwesomeSite";
|
||||
Accounts.emailTemplates.from = "AwesomeSite Admin <accounts@example.com>";
|
||||
|
||||
Accounts.emailTemplates.enrollAccount.subject = (user) => {
|
||||
return `Welcome to Awesome Town, ${user.profile.name}`;
|
||||
};
|
||||
|
||||
Accounts.emailTemplates.enrollAccount.text = (user, url) => {
|
||||
return (
|
||||
"You have been selected to participate in building a better future!" +
|
||||
" To activate your account, simply click the link below:\n\n" +
|
||||
url
|
||||
);
|
||||
};
|
||||
|
||||
Accounts.emailTemplates.resetPassword.from = () => {
|
||||
// Overrides the value set in `Accounts.emailTemplates.from` when resetting
|
||||
// passwords.
|
||||
return "AwesomeSite Password Reset <no-reply@example.com>";
|
||||
};
|
||||
Accounts.emailTemplates.verifyEmail = {
|
||||
subject() {
|
||||
return "Activate your account now!";
|
||||
},
|
||||
text(user, url) {
|
||||
return `Hey ${user}! Verify your e-mail by following this link: ${url}`;
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
<h3 id="enabling-2fa">Enable 2FA for this package</h3>
|
||||
|
||||
You can add 2FA to your login flow by
|
||||
using the package [accounts-2fa](../packages/accounts-2fa.md).
|
||||
You can find an example showing how this would look like [here](../packages/accounts-2fa.md#working-with-accounts-password).
|
||||
107
v3-docs/docs/api/app.md
Normal file
107
v3-docs/docs/api/app.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Mobile Configuration
|
||||
|
||||
|
||||
|
||||
If your Meteor application targets mobile platforms such as iOS or
|
||||
Android, you can configure your app's metadata and build process
|
||||
in a special top-level file called
|
||||
`mobile-config.js` which is *not* included in your application and is used only
|
||||
for this configuration.
|
||||
|
||||
The code snippet below is an example `mobile-config.js` file. The rest of this
|
||||
section will explain the specific API commands in greater detail.
|
||||
|
||||
```js
|
||||
// This section sets up some basic app metadata, the entire section is optional.
|
||||
App.info({
|
||||
id: 'com.example.matt.uber',
|
||||
name: 'über',
|
||||
description: 'Get über power in one button click',
|
||||
author: 'Matt Development Group',
|
||||
email: 'contact@example.com',
|
||||
website: 'http://example.com'
|
||||
});
|
||||
|
||||
// Set up resources such as icons and launch screens.
|
||||
App.icons({
|
||||
'iphone_2x': 'icons/icon-60@2x.png',
|
||||
'iphone_3x': 'icons/icon-60@3x.png',
|
||||
// More screen sizes and platforms...
|
||||
});
|
||||
|
||||
// Before Meteor 2.6 we had to pass device specific splash screens for iOS, but this behavior was dropped in favor of story board images.
|
||||
App.launchScreens({
|
||||
// iOS
|
||||
// For most cases you will only need to use the 'ios_universal' and 'ios_universal_3x'.
|
||||
'ios_universal': { src: 'splash/Default@2x.png', srcDarkMode: 'splash/Default@2x~dark.png' }, // (2732x2732) - All @2x devices, if device/mode specific is not declared
|
||||
'ios_universal_3x': 'splash/Default@3x.png', // (2208x2208) - All @3x devices, if device/mode specific is not declared
|
||||
|
||||
// If you still want to use a universal splash, but want to fine-tune for the device mode (landscape, portrait), then use the following keys:
|
||||
'Default@2x~universal~comany': 'splash/Default@2x~universal~comany.png', // (1278x2732) - All @2x devices in portrait mode.
|
||||
'Default@2x~universal~comcom': 'splash/Default@2x~universal~comcom.png', // (1334x750) - All @2x devices in landscape (narrow) mode.
|
||||
'Default@3x~universal~anycom': 'splash/Default@3x~universal~anycom.png', // (2208x1242) - All @3x devices in landscape (wide) mode.
|
||||
'Default@3x~universal~comany': 'splash/Default@3x~universal~comany.png', // (1242x2208) - All @3x devices in portrait mode.
|
||||
|
||||
// However, if you need to fine tune the splash screens for the device idiom (iPhone, iPad, etc).
|
||||
'Default@2x~iphone~anyany': 'splash/Default@2xiphoneanyany.png', // (1334x1334) - iPhone SE/6s/7/8/XR
|
||||
'Default@2x~iphone~comany': 'splash/Default@2xiphonecomany.png', // (750x1334) - iPhone SE/6s/7/8/XR - portrait mode
|
||||
'Default@2x~iphone~comcom': 'splash/Default@2xiphonecomcom.png', // (1334x750) - iPhone SE/6s/7/8/XR - landscape (narrow) mode
|
||||
'Default@3x~iphone~anyany': 'Default@3xiphoneanyany.png', // (2208x2208) - iPhone 6s Plus/7 Plus/8 Plus/X/XS/XS Max
|
||||
'Default@3x~iphone~anycom': { src: 'splash/Default@3xiphoneanycom.png', srcDarkMode: 'splash/Default@3xiphoneanycom~dark.png' }, // (2208x1242) - iPhone 6s Plus/7 Plus/8 Plus/X/XS/XS Max - landscape (wide) mode
|
||||
'Default@3x~iphone~comany': 'Default@3xiphonecomany.png', // (1242x2208) - iPhone 6s Plus/7 Plus/8 Plus/X/XS/XS Max - portrait mode
|
||||
'Default@2x~ipad~anyany': 'Default@2xipadanyany.png', // (2732x2732) - iPad Pro 12.9"/11"/10.5"/9.7"/7.9"
|
||||
'Default@2x~ipad~comany': 'Default@2xipadcomany.png', // (1278x2732) - iPad Pro 12.9"/11"/10.5"/9.7"/7.9" - portrait mode
|
||||
|
||||
// Android
|
||||
'android_universal': 'splash/android_universal.png', // (320x480)
|
||||
});
|
||||
|
||||
// Set PhoneGap/Cordova preferences.
|
||||
App.setPreference('BackgroundColor', '0xff0000ff');
|
||||
App.setPreference('HideKeyboardFormAccessoryBar', true);
|
||||
App.setPreference('Orientation', 'default');
|
||||
App.setPreference('Orientation', 'all', 'ios');
|
||||
|
||||
// Pass preferences for a particular PhoneGap/Cordova plugin.
|
||||
App.configurePlugin('com.phonegap.plugins.facebookconnect', {
|
||||
APP_ID: '1234567890',
|
||||
API_KEY: 'supersecretapikey'
|
||||
});
|
||||
|
||||
// Add custom tags for a particular PhoneGap/Cordova plugin to the end of the
|
||||
// generated config.xml. 'Universal Links' is shown as an example here.
|
||||
App.appendToConfig(`
|
||||
<universal-links>
|
||||
<host name="localhost:3000" />
|
||||
</universal-links>
|
||||
`);
|
||||
```
|
||||
|
||||
<ApiBox name="App.info" />
|
||||
<ApiBox name="App.setPreference" />
|
||||
<ApiBox name="App.accessRule" />
|
||||
|
||||
For example this Cordova whitelist syntax:
|
||||
|
||||
```xml
|
||||
<access origin="https://www.google-analytics.com" />
|
||||
<allow-navigation href="https://example.com" />
|
||||
```
|
||||
|
||||
is equivalent to:
|
||||
|
||||
```js
|
||||
App.accessRule('https://www.google-analytics.com');
|
||||
App.accessRule('https://example.com', { type: 'navigation' });
|
||||
```
|
||||
|
||||
<ApiBox name="App.configurePlugin" />
|
||||
|
||||
> 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`.
|
||||
|
||||
<ApiBox name="App.icons" />
|
||||
<ApiBox name="App.launchScreens" />
|
||||
<ApiBox name="App.appendToConfig" />
|
||||
<ApiBox name="App.addResourceFile" />
|
||||
|
||||
> 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**
|
||||
23
v3-docs/docs/api/assets.md
Normal file
23
v3-docs/docs/api/assets.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Assets
|
||||
|
||||
> 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.
|
||||
|
||||
`Assets` allows server code in a Meteor application to access static server
|
||||
assets, which are located in the `private` subdirectory of an application's
|
||||
tree. Assets are not processed as source files and are copied directly
|
||||
into your application's bundle.
|
||||
|
||||
<ApiBox name="Assets.getText"/>
|
||||
<ApiBox name="Assets.getBinary"/>
|
||||
<ApiBox name="Assets.absoluteFilePath"/>
|
||||
|
||||
Static server assets are included by placing them in the application's `private`
|
||||
subdirectory. For example, if an application's `private` subdirectory includes a
|
||||
directory called `nested` with a file called `data.txt` inside it, then server
|
||||
code can read `data.txt` by running:
|
||||
|
||||
```js
|
||||
const data = Assets.getText('nested/data.txt');
|
||||
```
|
||||
|
||||
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.
|
||||
6
v3-docs/docs/api/blaze.md
Normal file
6
v3-docs/docs/api/blaze.md
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
# Blaze
|
||||
|
||||
How to use Blaze, Meteor's reactive rendering engine.
|
||||
|
||||
This documentation has moved to the [Blaze Community Site](http://blazejs.org/api/templates).
|
||||
233
v3-docs/docs/api/check.md
Normal file
233
v3-docs/docs/api/check.md
Normal file
@@ -0,0 +1,233 @@
|
||||
# Check
|
||||
|
||||
Documentation on how to use check, Meteor's type checking library.
|
||||
|
||||
The `check` package includes pattern checking functions useful for checking the types and structure
|
||||
of variables and an [extensible library of patterns](#matchpatterns) to specify which types you are
|
||||
expecting.
|
||||
|
||||
To add `check` (or `Match`) to your application, run this command in your terminal:
|
||||
|
||||
```bash
|
||||
meteor add check
|
||||
```
|
||||
|
||||
<ApiBox name="check" hasCustomExample/>
|
||||
|
||||
Meteor methods and publish functions can take arbitrary [EJSON](./EJSON.md) types as arguments, but most
|
||||
functions expect their arguments to be of a particular type. `check` is a lightweight function for
|
||||
checking that arguments and other values are of the expected type. For example:
|
||||
|
||||
```js [server.js]
|
||||
import { check } from "meteor/check";
|
||||
import { Meteor } from "meteor/meteor";
|
||||
Meteor.publish("chatsInRoom", function (roomId) {
|
||||
// Make sure `roomId` is a string, not an arbitrary Mongo selector object.
|
||||
check(roomId, String);
|
||||
return Chats.find({ room: roomId });
|
||||
});
|
||||
|
||||
Meteor.methods({
|
||||
addChat(roomId, message) {
|
||||
check(roomId, String);
|
||||
check(message, {
|
||||
text: String,
|
||||
timestamp: Date,
|
||||
// Optional, but if present must be an array of strings.
|
||||
tags: Match.Maybe([String]),
|
||||
});
|
||||
|
||||
// Do something with the message...
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
If the match fails, `check` throws a `Match.Error` describing how it failed. If
|
||||
this error gets sent over the wire to the client, it will appear only as
|
||||
`Meteor.Error(400, 'Match Failed')`. The failure details will be written to the
|
||||
server logs but not revealed to the client.
|
||||
|
||||
<ApiBox name="Match.test" hasCustomExample/>
|
||||
|
||||
`Match.test` can be used to identify if a variable has a certain structure.
|
||||
|
||||
```js
|
||||
import { Match } from "meteor/check";
|
||||
|
||||
// Will return true for `{ foo: 1, bar: 'hello' }` or similar.
|
||||
Match.test(value, { foo: Match.Integer, bar: String });
|
||||
|
||||
// Will return true if `value` is a string.
|
||||
Match.test(value, String);
|
||||
|
||||
// Will return true if `value` is a string or an array of numbers.
|
||||
Match.test(value, Match.OneOf(String, [Number]));
|
||||
```
|
||||
|
||||
This can be useful if you have a function that accepts several different kinds
|
||||
of objects, and you want to determine which was passed in.
|
||||
|
||||
## Match Patterns { #matchpatterns }
|
||||
|
||||
The following patterns can be used as pattern arguments to
|
||||
[`check`](#check) and `Match.test`:
|
||||
|
||||
### `Match.Any`
|
||||
|
||||
Matches any value.
|
||||
|
||||
```js
|
||||
import { Match } from "meteor/check";
|
||||
// Will return true for any value.
|
||||
Match.test(value, Match.Any);
|
||||
```
|
||||
|
||||
### `String`, `Number`, `Boolean`, `undefined`, `null`
|
||||
|
||||
Matches a primitive of the given type.
|
||||
|
||||
```js
|
||||
import { Match } from "meteor/check";
|
||||
let result;
|
||||
// Will return true if `value` is a string.
|
||||
result = Match.test(value, String);
|
||||
console.log(result); // true
|
||||
|
||||
// Will return true if `value` is a number.
|
||||
result = Match.test(value, Number);
|
||||
console.log(result); // true
|
||||
|
||||
// Will return true if `value` is a boolean.
|
||||
result = Match.test(value, Boolean);
|
||||
console.log(result); // true
|
||||
```
|
||||
|
||||
### `Match.Integer`
|
||||
|
||||
Matches a signed 32-bit integer. Doesn't match `Infinity`, `-Infinity`, or `NaN`.
|
||||
|
||||
```js
|
||||
import { Match } from "meteor/check";
|
||||
let result;
|
||||
// Will return true if `value` is an integer.
|
||||
result = Match.test(value, Match.Integer);
|
||||
console.log(result); // true
|
||||
```
|
||||
|
||||
### `[pattern]` { #arraypattern }
|
||||
|
||||
A one-element array matches an array of elements, each of which match
|
||||
_pattern_. For example, `[Number]` matches a (possibly empty) array of numbers;
|
||||
`[Match.Any]` matches any array.
|
||||
|
||||
```js
|
||||
import { Match } from "meteor/check";
|
||||
let result;
|
||||
// Will return true if `value` is an array of numbers.
|
||||
result = Match.test(value, [Number]);
|
||||
console.log(result); // true
|
||||
```
|
||||
|
||||
### `{ key1: pattern1, key2: pattern2, ... }`
|
||||
|
||||
Matches an Object with the given keys, with values matching the given patterns.
|
||||
If any _pattern_ is a `Match.Maybe` or `Match.Optional`, that key does not need to exist
|
||||
in the object. The value may not contain any keys not listed in the pattern.
|
||||
The value must be a plain Object with no special prototype.
|
||||
|
||||
```js
|
||||
import { Match } from "meteor/check";
|
||||
let result;
|
||||
// Will return true if `value` is an object with keys 'foo' and 'bar'.
|
||||
result = Match.test(value, { foo: String, bar: Number });
|
||||
console.log(result); // true
|
||||
```
|
||||
|
||||
### `Match.ObjectIncluding({ key1: pattern1, key2: pattern2, ... })`
|
||||
|
||||
Matches an Object with the given keys; the value may also have other keys
|
||||
with arbitrary values.
|
||||
|
||||
```js
|
||||
import { Match } from "meteor/check";
|
||||
let result;
|
||||
// Will return true if `value` is an object with keys 'foo' and 'bar'.
|
||||
result = Match.test(value, Match.ObjectIncluding({ foo: String, bar: Number }));
|
||||
console.log(result); // true
|
||||
```
|
||||
|
||||
### `Object`
|
||||
|
||||
Matches any plain Object with any keys; equivalent to
|
||||
`Match.ObjectIncluding({})`.
|
||||
|
||||
```js
|
||||
import { Match } from "meteor/check";
|
||||
let result;
|
||||
// Will return true if `value` is an object.
|
||||
result = Match.test(value, Object);
|
||||
console.log(result); // true
|
||||
```
|
||||
|
||||
### `Match.Maybe(pattern)` { #maybepattern }
|
||||
|
||||
Matches either `undefined`, `null`, or _pattern_. If used in an object, matches only if the key is
|
||||
not set as opposed to the value being set to `undefined` or `null`. This set of conditions was
|
||||
chosen because `undefined` arguments to Meteor Methods are converted to `null` when sent over the
|
||||
wire.
|
||||
|
||||
```js
|
||||
import { Match, check } from "meteor/check";
|
||||
// In an object
|
||||
const pattern = { name: Match.Maybe(String) };
|
||||
|
||||
check({ name: "something" }, pattern); // OK
|
||||
check({}, pattern); // OK
|
||||
check({ name: undefined }, pattern); // Throws an exception
|
||||
check({ name: null }, pattern); // Throws an exception
|
||||
|
||||
// Outside an object
|
||||
check(null, Match.Maybe(String)); // OK
|
||||
check(undefined, Match.Maybe(String)); // OK
|
||||
```
|
||||
|
||||
### `Match.Optional(pattern)` { #optionalpattern }
|
||||
|
||||
Behaves like `Match.Maybe` except it doesn't accept `null`. If used in an object, the behavior is
|
||||
identical to `Match.Maybe`.
|
||||
|
||||
### `Match.OneOf(pattern1, pattern2, ...)`
|
||||
|
||||
Matches any value that matches at least one of the provided patterns.
|
||||
|
||||
### Any constructor function (eg, `Date`)
|
||||
|
||||
Matches any element that is an instance of that type.
|
||||
|
||||
```js
|
||||
import { Match } from "meteor/check";
|
||||
let result;
|
||||
|
||||
// Will return true if `value` is a Date.
|
||||
result = Match.test(value, Date);
|
||||
```
|
||||
|
||||
### `Match.Where(condition)`
|
||||
|
||||
Calls the function _condition_ with the value as the argument. If _condition_
|
||||
returns true, this matches. If _condition_ throws a `Match.Error` or returns
|
||||
false, this fails. If _condition_ throws any other error, that error is thrown
|
||||
from the call to `check` or `Match.test`. Examples:
|
||||
|
||||
```js
|
||||
import { Match, check } from "meteor/check";
|
||||
|
||||
check(buffer, Match.Where(EJSON.isBinary));
|
||||
|
||||
const NonEmptyString = Match.Where((x) => {
|
||||
check(x, String);
|
||||
return x.length > 0;
|
||||
});
|
||||
|
||||
check(arg, NonEmptyString);
|
||||
```
|
||||
1156
v3-docs/docs/api/collections.md
Normal file
1156
v3-docs/docs/api/collections.md
Normal file
File diff suppressed because it is too large
Load Diff
166
v3-docs/docs/api/email.md
Normal file
166
v3-docs/docs/api/email.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# Email
|
||||
|
||||
The `email` package allows sending email from a Meteor app. To use it, add the
|
||||
package to your project by running in your terminal:
|
||||
|
||||
```bash
|
||||
meteor add email
|
||||
```
|
||||
|
||||
There are two ways on how to setup the package for sending e-mail.
|
||||
|
||||
First is to set `MAIL_URL`. The server reads from the `MAIL_URL` environment
|
||||
variable to determine how to send mail. The `MAIL_URL` should reference an
|
||||
[SMTP](https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol) server and
|
||||
use the form `smtp://USERNAME:PASSWORD@HOST:PORT` or
|
||||
`smtps://USERNAME:PASSWORD@HOST:PORT`. The `smtps://` form (the `s` is for
|
||||
"secure") should be used if the mail server requires TLS/SSL (and does not use
|
||||
`STARTTLS`) and is most common on port 465. Connections which start unencrypted
|
||||
prior to being upgraded to TLS/SSL (using `STARTTLS`) typically use port 587
|
||||
(and _sometimes_ 25) and should use `smtp://`. For more information see the
|
||||
[Nodemailer docs](https://nodemailer.com/smtp/)
|
||||
|
||||
Second, if you are using a one of the [supported services](https://community.nodemailer.com/smtp/well-known/#supported-services) you can setup the sending options in your app settings like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"packages": {
|
||||
"email": {
|
||||
"service": "Mailgun",
|
||||
"user": "postmaster@meteor.com",
|
||||
"password": "superDuperPassword"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The package will take care of the rest.
|
||||
|
||||
> If you use a supported service the package will try to match to supported service and use the stored settings instead.
|
||||
> You can force this by switching protocol like `smtp` to the name of the service.
|
||||
> Though you should only use this as a stop-gap measure and instead set the settings properly.
|
||||
|
||||
If neither option is set, `Email.send` outputs the message to standard output
|
||||
instead.
|
||||
|
||||
> Package setting is only available since Email v2.2
|
||||
|
||||
<ApiBox name="Email.send" hasCustomExample/>
|
||||
|
||||
You must provide the `from` option and at least one of `to`, `cc`, and `bcc`;
|
||||
all other options are optional.
|
||||
|
||||
`Email.send` only works on the server. Here is an example of how a
|
||||
client could use a server method call to send an email. (In an actual
|
||||
application, you'd need to be careful to limit the emails that a
|
||||
client could send, to prevent your server from being used as a relay
|
||||
by spammers.)
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [server.js]
|
||||
import { Meteor } from "meteor/meteor";
|
||||
import { Email } from "meteor/email";
|
||||
import { check } from "meteor/check";
|
||||
// Server: Define a method that the client can call.
|
||||
Meteor.methods({
|
||||
sendEmail({ to, from, subject, text }) {
|
||||
// Make sure that all arguments are strings.
|
||||
check([to, from, subject, text], [String]);
|
||||
|
||||
Email.send({ to, from, subject, text });
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```js [client.js]
|
||||
import { Meteor } from "meteor/meteor";
|
||||
// Client: Asynchronously send an email.
|
||||
Meteor.callAsync("sendEmail", {
|
||||
to: "Alice <alice@example.com>",
|
||||
from: "bob@example.com",
|
||||
subject: "Hello from Meteor!",
|
||||
text: "This is a test of Email.send.",
|
||||
});
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
<ApiBox name="Email.sendAsync" hasCustomExample/>
|
||||
|
||||
`sendAsync` only works on the server. It has the same behavior as `Email.send`, but returns a Promise.
|
||||
If you defined `Email.customTransport`, the `callAsync` method returns the return value from the `customTransport` method or a Promise, if this method is async.
|
||||
|
||||
```js
|
||||
// Server: Define a method that the client can call.
|
||||
Meteor.methods({
|
||||
sendEmail({ to, from, subject, text }) {
|
||||
// Make sure that all arguments are strings.
|
||||
check([to, from, subject, text], [String]);
|
||||
|
||||
return Email.sendAsync({ to, from, subject, text }).catch((err) => {
|
||||
// do something with the error
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
<ApiBox name="Email.hookSend" hasCustomExample/>
|
||||
|
||||
```js
|
||||
import { Email } from 'meteor/email'
|
||||
|
||||
Email.hookSend(({to}) => {
|
||||
if (to === 'ceo@company.com') {
|
||||
console.log(`Let's not send email to the CEO`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
```
|
||||
|
||||
`hookSend` is a convenient hook if you want to: prevent sending certain emails,
|
||||
send emails via your own integration instead of the default one provided by
|
||||
Meteor, or do something else with the data. This is especially useful
|
||||
if you want to intercept emails sent by core packages like accounts-password
|
||||
or other packages where you can't modify the email code.
|
||||
|
||||
The hook function will receive an object with the options for Nodemailer.
|
||||
|
||||
|
||||
<ApiBox name="Email.customTransport" hasCustomExample/>
|
||||
|
||||
> `Email.customTransport` is only available since Email v2.2
|
||||
|
||||
There are scenarios when you have your own transport set up, be it an SDK
|
||||
for your mailing service or something else. This is where `customTransport`
|
||||
comes in. If you set this function all sending events will be passed to it
|
||||
(after `hookSend` is run) with an object of the options passed into `send`
|
||||
function with addition of `packageSettings` key which will pass in package settings
|
||||
set in your app settings (if any). It is up to you what you do in that function
|
||||
as it will override the original sending function.
|
||||
|
||||
Here is a simple example with Mailgun:
|
||||
|
||||
```javascript
|
||||
import { Email } from 'meteor/email'
|
||||
import { Log } from 'meteor/logging'
|
||||
import Mailgun from 'mailgun-js'
|
||||
|
||||
Email.customTransport = (data) => {
|
||||
// `options.packageSettings` are settings from `Meteor.settings.packages.email`
|
||||
// The rest of the options are from Email.send options
|
||||
const mailgun = Mailgun({ apiKey: data.packageSettings.mailgun.privateKey, domain: 'mg.mygreatapp.com' })
|
||||
|
||||
// Since the data object that we receive already includes the correct key names for sending
|
||||
// we can just pass it to the mailgun sending message.
|
||||
mailgun.messages().send(data, (error, body) => {
|
||||
if (error) Log.error(error)
|
||||
if (body) Log.info(body)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
> Note that this also overrides the development display of messages in console
|
||||
> so you might want to differentiate between production and development for
|
||||
> setting this function.
|
||||
2
v3-docs/docs/api/index.md
Normal file
2
v3-docs/docs/api/index.md
Normal file
@@ -0,0 +1,2 @@
|
||||
<ApiMap />
|
||||
|
||||
958
v3-docs/docs/api/meteor.md
Normal file
958
v3-docs/docs/api/meteor.md
Normal file
@@ -0,0 +1,958 @@
|
||||
# Meteor API
|
||||
|
||||
Meteor global object has many functions and properties for handling utilities, network and much more.
|
||||
|
||||
### Core APIs {#core}
|
||||
|
||||
<ApiBox name="Meteor.startup" hasCustomExample/>
|
||||
|
||||
On a server, the function will run as soon as the server process is
|
||||
finished starting. On a client, the function will run as soon as the DOM
|
||||
is ready. Code wrapped in `Meteor.startup` always runs after all app
|
||||
files have loaded, so you should put code here if you want to access
|
||||
shared variables from other files.
|
||||
|
||||
The `startup` callbacks are called in the same order as the calls to
|
||||
`Meteor.startup` were made.
|
||||
|
||||
On a client, `startup` callbacks from packages will be called
|
||||
first, followed by `<body>` templates from your `.html` files,
|
||||
followed by your application code.
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [server.js]
|
||||
import { Meteor } from "meteor/meteor";
|
||||
import { LinksCollection } from "/imports/api/links";
|
||||
|
||||
Meteor.startup(async () => {
|
||||
// If the Links collection is empty, add some data.
|
||||
if ((await LinksCollection.find().countAsync()) === 0) {
|
||||
await LinksCollection.insertAsync({
|
||||
title: "Do the Tutorial",
|
||||
url: "https://www.meteor.com/tutorials/react/creating-an-app",
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```js [client.js]
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { Meteor } from "meteor/meteor";
|
||||
import { App } from "/imports/ui/App";
|
||||
|
||||
// Setup react root
|
||||
Meteor.startup(() => {
|
||||
const container = document.getElementById("react-target");
|
||||
const root = createRoot(container);
|
||||
root.render(<App />);
|
||||
});
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
<ApiBox name="Meteor.wrapAsync" />
|
||||
<ApiBox name="Meteor.defer" />
|
||||
<ApiBox name="Meteor.absoluteUrl" />
|
||||
<ApiBox name="Meteor.settings" />
|
||||
<ApiBox name="Meteor.release" />
|
||||
|
||||
<ApiBox name="Meteor.isClient" />
|
||||
|
||||
<ApiBox name="Meteor.isServer" />
|
||||
|
||||
::: danger
|
||||
`Meteor.isServer` can be used to limit where code runs, but it does not prevent code from
|
||||
being sent to the client. Any sensitive code that you don’t want served to the client,
|
||||
such as code containing passwords or authentication mechanisms,
|
||||
should be kept in the `server` directory.
|
||||
:::
|
||||
|
||||
<ApiBox name="Meteor.isCordova" />
|
||||
<ApiBox name="Meteor.isDevelopment" />
|
||||
<ApiBox name="Meteor.isProduction" />
|
||||
<ApiBox name="Meteor.isModern" />
|
||||
<ApiBox name="Meteor.isTest" />
|
||||
<ApiBox name="Meteor.isAppTest" />
|
||||
<ApiBox name="Meteor.isPackageTest" />
|
||||
|
||||
<ApiBox name="Meteor.gitCommitHash" />
|
||||
|
||||
### Method APIs {#methods}
|
||||
|
||||
Meteor Methods are Remote Procedure Calls (RPCs) are functions defined by `Meteor.methods`
|
||||
and called by [`Meteor.call`](#Meteor-call).
|
||||
|
||||
<ApiBox name="Meteor.methods" hasCustomExample/>
|
||||
|
||||
The most basic way to define a method is to provide a function:
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [server.js]
|
||||
import { Meteor } from "meteor/meteor";
|
||||
|
||||
Meteor.methods({
|
||||
sum(a, b) {
|
||||
return a + b;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```js [client.js]
|
||||
import { Meteor } from "meteor/meteor";
|
||||
|
||||
const result = await Meteor.callAsync("sum", 1, 2);
|
||||
console.log(result); // 3
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
You can use `Meteor.methods` to define multiple methods at once.
|
||||
|
||||
You can think of `Meteor.methods` as a way of defining a remote object that is your server API.
|
||||
|
||||
A more complete example:
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [server.js]
|
||||
import { Meteor } from "meteor/meteor";
|
||||
import { check } from "meteor/check";
|
||||
import { LinksCollection } from "/imports/api/links";
|
||||
|
||||
Meteor.methods({
|
||||
async addLink(link) {
|
||||
check(link, String); // check if the link is a string
|
||||
|
||||
// Do stuff...
|
||||
const linkID = await LinksCollection.insertAsync(link);
|
||||
if (/* you want to throw an error */) {
|
||||
throw new Meteor.Error('Something is wrong', "Some details");
|
||||
}
|
||||
|
||||
return linkID;
|
||||
},
|
||||
|
||||
bar() {
|
||||
// Do other stuff...
|
||||
return 'baz';
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```js [client.js]
|
||||
import React from "react";
|
||||
import { Meteor } from "meteor/meteor";
|
||||
|
||||
function Component() {
|
||||
const addLink = () =>
|
||||
Meteor.callAsync(
|
||||
"addLink",
|
||||
"https://www.meteor.com/tutorials/react/creating-an-app"
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={addLink}>Add Link</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Calling `methods` on the server defines functions that can be called remotely by
|
||||
clients. They should return an [EJSON](./EJSON)-able value or throw an
|
||||
exception. Inside your method invocation, `this` is bound to a method
|
||||
invocation object, which provides the following:
|
||||
|
||||
- `isSimulation`: a boolean value, true if this invocation is a stub.
|
||||
- `unblock`: when called, allows the next method from this client to
|
||||
begin running.
|
||||
- `userId`: the id of the current user.
|
||||
- `setUserId`: a function that associates the current client with a user.
|
||||
- `connection`: on the server, the [connection](#Meteor-onConnection) this method call was received on.
|
||||
|
||||
Calling `methods` on the client defines _stub_ functions associated with
|
||||
server methods of the same name. You don't have to define a stub for
|
||||
your method if you don't want to. In that case, method calls are just
|
||||
like remote procedure calls in other systems, and you'll have to wait
|
||||
for the results from the server.
|
||||
|
||||
If you do define a stub, when a client invokes a server method it will
|
||||
also run its stub in parallel. On the client, the return value of a
|
||||
stub is ignored. Stubs are run for their side-effects: they are
|
||||
intended to _simulate_ the result of what the server's method will do,
|
||||
but without waiting for the round trip delay. If a stub throws an
|
||||
exception it will be logged to the console.
|
||||
|
||||
You use methods all the time, because the database mutators
|
||||
([`insert`](./collections#Mongo-Collection-insert), [`update`](./collections#Mongo-Collection-update), [`remove`](./collections#Mongo-Collection-remove)) are implemented
|
||||
as methods. When you call any of these functions on the client, you're invoking
|
||||
their stub version that update the local cache, and sending the same write
|
||||
request to the server. When the server responds, the client updates the local
|
||||
cache with the writes that actually occurred on the server.
|
||||
|
||||
You don't have to put all your method definitions into a single `Meteor.methods`
|
||||
call; you may call it multiple times, as long as each method has a unique name.
|
||||
|
||||
If a client calls a method and is disconnected before it receives a response,
|
||||
it will re-call the method when it reconnects. This means that a client may
|
||||
call a method multiple times when it only means to call it once. If this
|
||||
behavior is problematic for your method, consider attaching a unique ID
|
||||
to each method call on the client, and checking on the server whether a call
|
||||
with this ID has already been made. Alternatively, you can use
|
||||
[`Meteor.apply`](#Meteor-apply) with the noRetry option set to true.
|
||||
|
||||
Read more about methods and how to use them in the [Methods](http://guide.meteor.com/methods.html) article in the Meteor Guide.
|
||||
|
||||
<ApiBox name="Meteor.isAsyncCall" hasCustomExample/>
|
||||
|
||||
This method can be used to determine if the current method invocation is
|
||||
asynchronous. It returns true if the method is running on the server and came from
|
||||
an async call(`Meteor.callAsync`)
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [server.js]
|
||||
import { Meteor } from "meteor/meteor";
|
||||
|
||||
Meteor.methods({
|
||||
async foo() {
|
||||
return Meteor.isAsyncCall();
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```js [client.js]
|
||||
import { Meteor } from "meteor/meteor";
|
||||
|
||||
const result = await Meteor.callAsync("foo");
|
||||
console.log(result); // true
|
||||
|
||||
Meteor.call("foo", (err, result) => {
|
||||
console.log(result); // false
|
||||
});
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## this.userId {#methods-userId}
|
||||
|
||||
The user id is an arbitrary string — typically the id of the user record
|
||||
in the database. You can set it with the `setUserId` function. If you're using
|
||||
the [Meteor accounts system](./accounts.md) then this is handled for you.
|
||||
|
||||
```js
|
||||
import { Meteor } from "meteor/meteor";
|
||||
|
||||
Meteor.methods({
|
||||
foo() {
|
||||
console.log(this.userId);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## this.setUserId {#methods-setUserId}
|
||||
|
||||
Call this function to change the currently logged-in user on the
|
||||
connection that made this method call. This simply sets the value of
|
||||
`userId` for future method calls received on this connection. Pass
|
||||
`null` to log out the connection.
|
||||
|
||||
If you are using the [built-in Meteor accounts system](./accounts) then this
|
||||
should correspond to the `_id` field of a document in the
|
||||
[`Meteor.users`](./accounts.md#Meteor-user) collection.
|
||||
|
||||
`setUserId` is not retroactive. It affects the current method call and
|
||||
any future method calls on the connection. Any previous method calls on
|
||||
this connection will still see the value of `userId` that was in effect
|
||||
when they started.
|
||||
|
||||
If you also want to change the logged-in user on the client, then after calling
|
||||
`setUserId` on the server, call `Meteor.connection.setUserId(userId)` on the
|
||||
client.
|
||||
|
||||
```js
|
||||
import { Meteor } from "meteor/meteor";
|
||||
|
||||
Meteor.methods({
|
||||
foo() {
|
||||
this.setUserId("some-id");
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## this.connection {#methods-connection}
|
||||
|
||||
Access inside a method invocation. The [connection](#Meteor-onConnection) that this method was received on.
|
||||
null if the method is not associated with a connection,
|
||||
eg. a server initiated method call. Calls to methods
|
||||
made from a server method which was in turn initiated from the client share the same
|
||||
connection.
|
||||
|
||||
<ApiBox name="Meteor.Error" />
|
||||
|
||||
For example:
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [server.js]
|
||||
import { Meteor } from "meteor/meteor";
|
||||
// on the server, pick a code unique to this error
|
||||
// the reason field should be a useful debug message
|
||||
Meteor.methods({
|
||||
methodName() {
|
||||
throw new Meteor.Error(
|
||||
"logged-out",
|
||||
"The user must be logged in to post a comment."
|
||||
);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```js [client.js]
|
||||
import { Meteor } from "meteor/meteor";
|
||||
// on the client
|
||||
Meteor.call("methodName", function (error) {
|
||||
// identify the error
|
||||
if (error && error.error === "logged-out") {
|
||||
// show a nice error message
|
||||
Session.set("errorMessage", "Please log in to post a comment.");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
If you want to return an error from a method, throw an exception. Methods can
|
||||
throw any kind of exception. But `Meteor.Error` is the only kind of error that
|
||||
a server will send to the client. If a method function throws a different
|
||||
exception, then it will be mapped to a sanitized version on the
|
||||
wire. Specifically, if the `sanitizedError` field on the thrown error is set to
|
||||
a `Meteor.Error`, then that error will be sent to the client. Otherwise, if no
|
||||
sanitized version is available, the client gets
|
||||
`Meteor.Error(500, 'Internal server error')`.
|
||||
|
||||
<ApiBox name="Meteor.call" hasCustomExample/>
|
||||
|
||||
This is how to invoke a method with a sync stub. It will run the method on the server. If a
|
||||
stub is available, it will also run the stub on the client. (See also
|
||||
[`Meteor.apply`](#Meteor-apply), which is identical to `Meteor.call` except that
|
||||
you specify the parameters as an array instead of as separate arguments and you
|
||||
can specify a few options controlling how the method is executed.)
|
||||
|
||||
If you include a callback function as the last argument (which can't be
|
||||
an argument to the method, since functions aren't serializable), the
|
||||
method will run asynchronously: it will return nothing in particular and
|
||||
will not throw an exception. When the method is complete (which may or
|
||||
may not happen before `Meteor.call` returns), the callback will be
|
||||
called with two arguments: `error` and `result`. If an error was thrown,
|
||||
then `error` will be the exception object. Otherwise, `error` will be
|
||||
`undefined` and the return value (possibly `undefined`) will be in `result`.
|
||||
|
||||
```js
|
||||
// Asynchronous call
|
||||
Meteor.call('foo', 1, 2, (error, result) => { ... });
|
||||
```
|
||||
|
||||
If you do not pass a callback on the server, the method invocation will
|
||||
block until the method is complete. It will eventually return the
|
||||
return value of the method, or it will throw an exception if the method
|
||||
threw an exception. (Possibly mapped to 500 Server Error if the
|
||||
exception happened remotely and it was not a `Meteor.Error` exception.)
|
||||
|
||||
```js
|
||||
// Synchronous call
|
||||
const result = Meteor.call("foo", 1, 2);
|
||||
```
|
||||
|
||||
On the client, if you do not pass a callback and you are not inside a
|
||||
stub, `call` will return `undefined`, and you will have no way to get
|
||||
the return value of the method. That is because the client doesn't have
|
||||
fibers, so there is not actually any way it can block on the remote
|
||||
execution of a method.
|
||||
|
||||
Finally, if you are inside a stub on the client and call another
|
||||
method, the other method is not executed (no RPC is generated, nothing
|
||||
"real" happens). If that other method has a stub, that stub stands in
|
||||
for the method and is executed. The method call's return value is the
|
||||
return value of the stub function. The client has no problem executing
|
||||
a stub synchronously, and that is why it's okay for the client to use
|
||||
the synchronous `Meteor.call` form from inside a method body, as
|
||||
described earlier.
|
||||
|
||||
Meteor tracks the database writes performed by methods, both on the client and
|
||||
the server, and does not invoke `asyncCallback` until all of the server's writes
|
||||
replace the stub's writes in the local cache. In some cases, there can be a lag
|
||||
between the method's return value being available and the writes being visible:
|
||||
for example, if another method still outstanding wrote to the same document, the
|
||||
local cache may not be up to date until the other method finishes as well. If
|
||||
you want to process the method's result as soon as it arrives from the server,
|
||||
even if the method's writes are not available yet, you can specify an
|
||||
`onResultReceived` callback to [`Meteor.apply`](#Meteor-apply).
|
||||
|
||||
<ApiBox name="Meteor.callAsync" />
|
||||
|
||||
`Meteor.callAsync` is just like `Meteor.call`, except that it'll return a promise that you need to solve to get the result.
|
||||
|
||||
<ApiBox name="Meteor.apply" />
|
||||
|
||||
`Meteor.apply` is just like `Meteor.call`, except that the method arguments are
|
||||
passed as an array rather than directly as arguments, and you can specify
|
||||
options about how the client executes the method.
|
||||
|
||||
<ApiBox name="Meteor.applyAsync" />
|
||||
|
||||
`Meteor.applyAsync` is just like `Meteor.apply`, except it is an async function, and it will consider that the stub is async.
|
||||
|
||||
### Publish and subscribe {#pubsub}
|
||||
|
||||
These functions control how Meteor servers publish sets of records and
|
||||
how clients can subscribe to those sets.
|
||||
|
||||
<ApiBox name="Meteor.publish" hasCustomExample>
|
||||
To publish records to clients, call `Meteor.publish` on the server with
|
||||
two parameters: the name of the record set, and a _publish function_
|
||||
that Meteor will call each time a client subscribes to the name.
|
||||
|
||||
Publish functions can return a
|
||||
[`Collection.Cursor`](./collections.md#mongo_cursor), in which case Meteor
|
||||
will publish that cursor's documents to each subscribed client. You can
|
||||
also return an array of `Collection.Cursor`s, in which case Meteor will
|
||||
publish all of the cursors.
|
||||
|
||||
::: warning
|
||||
If you return multiple cursors in an array, they currently must all be from
|
||||
different collections. We hope to lift this restriction in a future release.
|
||||
:::
|
||||
|
||||
</ApiBox>
|
||||
|
||||
|
||||
|
||||
```js
|
||||
import { Meteor } from "meteor/meteor";
|
||||
import { check } from "meteor/check";
|
||||
import { Rooms } from "/imports/api/Rooms";
|
||||
import { Messages } from "/imports/api/Messages";
|
||||
|
||||
// Server: Publish the `Rooms` collection, minus secret info...
|
||||
Meteor.publish("rooms", function () {
|
||||
return Rooms.find(
|
||||
{},
|
||||
{
|
||||
fields: { secretInfo: 0 },
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// ...and publish secret info for rooms where the logged-in user is an admin. If
|
||||
// the client subscribes to both publications, the records are merged together
|
||||
// into the same documents in the `Rooms` collection. Note that currently object
|
||||
// values are not recursively merged, so the fields that differ must be top
|
||||
// level fields.
|
||||
Meteor.publish("adminSecretInfo", function () {
|
||||
return Rooms.find(
|
||||
{ admin: this.userId },
|
||||
{
|
||||
fields: { secretInfo: 1 },
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Publish dependent documents and simulate joins.
|
||||
Meteor.publish("roomAndMessages", function (roomId) {
|
||||
check(roomId, String);
|
||||
|
||||
return [
|
||||
Rooms.find(
|
||||
{ _id: roomId },
|
||||
{
|
||||
fields: { secretInfo: 0 },
|
||||
}
|
||||
),
|
||||
Messages.find({ roomId }),
|
||||
];
|
||||
});
|
||||
```
|
||||
|
||||
Alternatively, a publish function can directly control its published record set
|
||||
by calling the functions [`added`](#Subscription-added) (to add a new document to the
|
||||
published record set), [`changed`](#Subscription-changed) (to change or clear some
|
||||
fields on a document already in the published record set), and
|
||||
[`removed`](#Subscription-removed) (to remove documents from the published record
|
||||
set). These methods are provided by `this` in your publish function.
|
||||
|
||||
If a publish function does not return a cursor or array of cursors, it is
|
||||
assumed to be using the low-level `added`/`changed`/`removed` interface, and it
|
||||
**must also call [`ready`](#Subscription-ready) once the initial record set is
|
||||
complete**.
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [collections.js]
|
||||
import { Mongo } from "meteor/mongo";
|
||||
|
||||
export const Rooms = new Mongo.Collection("rooms");
|
||||
export const SecretData = new Mongo.Collection("messages");
|
||||
```
|
||||
|
||||
```js [server.js]
|
||||
import { Meteor } from "meteor/meteor";
|
||||
import { check } from "meteor/check";
|
||||
import { Rooms, SecretData } from "/imports/api/collections";
|
||||
|
||||
// Publish the current size of a collection.
|
||||
Meteor.publish("countsByRoom", function (roomId) {
|
||||
check(roomId, String);
|
||||
|
||||
let count = 0;
|
||||
let initializing = true;
|
||||
|
||||
// `observeChanges` only returns after the initial `added` callbacks have run.
|
||||
// Until then, we don't want to send a lot of `changed` messages—hence
|
||||
// tracking the `initializing` state.
|
||||
const handle = Messages.find({ roomId }).observeChanges({
|
||||
added: (id) => {
|
||||
count += 1;
|
||||
|
||||
if (!initializing) {
|
||||
this.changed("counts", roomId, { count });
|
||||
}
|
||||
},
|
||||
|
||||
removed: (id) => {
|
||||
count -= 1;
|
||||
this.changed("counts", roomId, { count });
|
||||
},
|
||||
|
||||
// We don't care about `changed` events.
|
||||
});
|
||||
|
||||
// Instead, we'll send one `added` message right after `observeChanges` has
|
||||
// returned, and mark the subscription as ready.
|
||||
initializing = false;
|
||||
this.added("counts", roomId, { count });
|
||||
this.ready();
|
||||
|
||||
// Stop observing the cursor when the client unsubscribes. Stopping a
|
||||
// subscription automatically takes care of sending the client any `removed`
|
||||
// messages.
|
||||
this.onStop(() => handle.stop());
|
||||
});
|
||||
|
||||
// Sometimes publish a query, sometimes publish nothing.
|
||||
Meteor.publish("secretData", function () {
|
||||
if (this.userId === "superuser") {
|
||||
return SecretData.find();
|
||||
} else {
|
||||
// Declare that no data is being published. If you leave this line out,
|
||||
// Meteor will never consider the subscription ready because it thinks
|
||||
// you're using the `added/changed/removed` interface where you have to
|
||||
// explicitly call `this.ready`.
|
||||
return [];
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```js [client.js]
|
||||
import { Meteor } from "meteor/meteor";
|
||||
import { Mongo } from "meteor/mongo";
|
||||
import { Session } from "meteor/session";
|
||||
// Declare a collection to hold the count object.
|
||||
const Counts = new Mongo.Collection("counts");
|
||||
|
||||
// Subscribe to the count for the current room.
|
||||
Tracker.autorun(() => {
|
||||
Meteor.subscribe("countsByRoom", Session.get("roomId"));
|
||||
});
|
||||
|
||||
// Use the new collection.
|
||||
const roomCount = Counts.findOne(Session.get("roomId")).count;
|
||||
console.log(`Current room has ${roomCount} messages.`);
|
||||
```
|
||||
|
||||
::: warning
|
||||
|
||||
Meteor will emit a warning message if you call `Meteor.publish` in a
|
||||
project that includes the `autopublish` package. Your publish function
|
||||
will still work.
|
||||
|
||||
:::
|
||||
|
||||
Read more about publications and how to use them in the
|
||||
[Data Loading](http://guide.meteor.com/data-loading.html) article in the Meteor Guide.
|
||||
|
||||
<ApiBox name="Subscription#userId" />
|
||||
|
||||
This is constant. However, if the logged-in user changes, the publish
|
||||
function is rerun with the new value, assuming it didn't throw an error at the previous run.
|
||||
|
||||
<ApiBox name="Subscription#added" />
|
||||
<ApiBox name="Subscription#changed" />
|
||||
<ApiBox name="Subscription#removed" />
|
||||
<ApiBox name="Subscription#ready" />
|
||||
<ApiBox name="Subscription#onStop" />
|
||||
|
||||
If you call [`observe`](./collections.md#Mongo-Cursor-observe) or [`observeChanges`](./collections.md#Mongo-Cursor-observeChanges) in your
|
||||
publish handler, this is the place to stop the observes.
|
||||
|
||||
<ApiBox name="Subscription#error" />
|
||||
<ApiBox name="Subscription#stop" />
|
||||
<ApiBox name="Subscription#connection" />
|
||||
|
||||
<ApiBox name="Meteor.subscribe" hasCustomExample/>
|
||||
|
||||
When you subscribe to a record set, it tells the server to send records to the
|
||||
client. The client stores these records in local [Minimongo collections](./collections.md), with the same name as the `collection`
|
||||
argument used in the publish handler's [`added`](#Subscription-added),
|
||||
[`changed`](#Subscription-changed), and [`removed`](#Subscription-removed)
|
||||
callbacks. Meteor will queue incoming records until you declare the
|
||||
[`Mongo.Collection`](./collections.md) on the client with the matching
|
||||
collection name.
|
||||
|
||||
```js
|
||||
// It's okay to subscribe (and possibly receive data) before declaring the
|
||||
// client collection that will hold it. Assume 'allPlayers' publishes data from
|
||||
// the server's 'players' collection.
|
||||
Meteor.subscribe("allPlayers");
|
||||
|
||||
// The client queues incoming 'players' records until the collection is created:
|
||||
const Players = new Mongo.Collection("players");
|
||||
```
|
||||
|
||||
The client will see a document if the document is currently in the published
|
||||
record set of any of its subscriptions. If multiple publications publish a
|
||||
document with the same `_id` for the same collection the documents are merged for
|
||||
the client. If the values of any of the top level fields conflict, the resulting
|
||||
value will be one of the published values, chosen arbitrarily.
|
||||
|
||||
::: warning
|
||||
Currently, when multiple subscriptions publish the same document _only the top
|
||||
level fields_ are compared during the merge. This means that if the documents
|
||||
include different sub-fields of the same top level field, not all of them will
|
||||
be available on the client. We hope to lift this restriction in a future release.
|
||||
:::
|
||||
|
||||
The `onReady` callback is called with no arguments when the server [marks the subscription as ready](#Subscription-ready). The `onStop` callback is called with
|
||||
a [`Meteor.Error`](#Meteor-Error) if the subscription fails or is terminated by
|
||||
the server. If the subscription is stopped by calling `stop` on the subscription
|
||||
handle or inside the publication, `onStop` is called with no arguments.
|
||||
|
||||
`Meteor.subscribe` returns a subscription handle, which is an object with the
|
||||
following properties:
|
||||
|
||||
```ts
|
||||
import { Meteor } from "meteor/meteor";
|
||||
const handle = Meteor.subscribe("allPlayers");
|
||||
|
||||
handle.ready(); // True when the server has marked the subscription as ready
|
||||
|
||||
handle.stop(); // Stop this subscription and unsubscribe from the server
|
||||
|
||||
handle.subscriptionId; // The id of the subscription this handle is for.
|
||||
```
|
||||
|
||||
When you run Meteor.subscribe inside of Tracker.autorun, the handles you get will always have the same subscriptionId field.
|
||||
You can use this to deduplicate subscription handles if you are storing them in some data structure.
|
||||
|
||||
If you call `Meteor.subscribe` within a reactive computation,
|
||||
for example using
|
||||
[`Tracker.autorun`](./Tracker#Tracker-autorun), the subscription will automatically be
|
||||
cancelled when the computation is invalidated or stopped; it is not necessary
|
||||
to call `stop` on
|
||||
subscriptions made from inside `autorun`. However, if the next iteration
|
||||
of your run function subscribes to the same record set (same name and
|
||||
parameters), Meteor is smart enough to skip a wasteful
|
||||
unsubscribe/resubscribe. For example:
|
||||
|
||||
```js
|
||||
Tracker.autorun(() => {
|
||||
Meteor.subscribe("chat", { room: Session.get("currentRoom") });
|
||||
Meteor.subscribe("privateMessages");
|
||||
});
|
||||
```
|
||||
|
||||
This subscribes you to the chat messages in the current room and to your private
|
||||
messages. When you change rooms by calling `Session.set('currentRoom',
|
||||
'newRoom')`, Meteor will subscribe to the new room's chat messages,
|
||||
unsubscribe from the original room's chat messages, and continue to
|
||||
stay subscribed to your private messages.
|
||||
|
||||
## Publication strategies
|
||||
|
||||
> The following features are available from Meteor 2.4 or `ddp-server@2.5.0`
|
||||
|
||||
Once you start scaling your application you might want to have more control on how the data from publications is being handled on the client.
|
||||
There are three publications strategies:
|
||||
|
||||
#### SERVER_MERGE
|
||||
|
||||
`SERVER_MERGE` is the default strategy. When using this strategy, the server maintains a copy of all data a connection is subscribed to.
|
||||
This allows us to only send deltas over multiple publications.
|
||||
|
||||
#### NO_MERGE_NO_HISTORY
|
||||
|
||||
The `NO_MERGE_NO_HISTORY` strategy results in the server sending all publication data directly to the client.
|
||||
It does not remember what it has previously sent to client and will not trigger removed messages when a subscription is stopped.
|
||||
This should only be chosen for special use cases like send-and-forget queues.
|
||||
|
||||
#### NO_MERGE
|
||||
|
||||
`NO_MERGE` is similar to `NO_MERGE_NO_HISTORY` but the server will remember the IDs it has
|
||||
sent to the client so it can remove them when a subscription is stopped.
|
||||
This strategy can be used when a collection is only used in a single publication.
|
||||
|
||||
When `NO_MERGE` is selected the client will be handling gracefully duplicate events without throwing an exception.
|
||||
Specifically:
|
||||
|
||||
- When we receive an added message for a document that is already present in the client's collection, it will be changed.
|
||||
- When we receive a change message for a document that is not in the client's collection, it will be added.
|
||||
- When we receive a removed message for a document that is not in the client's collection, nothing will happen.
|
||||
|
||||
You can import the publication strategies from `DDPServer`.
|
||||
|
||||
```js
|
||||
import { DDPServer } from "meteor/ddp-server";
|
||||
|
||||
const { SERVER_MERGE, NO_MERGE_NO_HISTORY, NO_MERGE } =
|
||||
DDPServer.publicationStrategies;
|
||||
```
|
||||
|
||||
You can use the following methods to set or get the publication strategy for publications:
|
||||
|
||||
<ApiBox name="setPublicationStrategy" hasCustomExample/>
|
||||
|
||||
For the `foo` collection, you can set the `NO_MERGE` strategy as shown:
|
||||
|
||||
```js
|
||||
import { DDPServer } from "meteor/ddp-server";
|
||||
Meteor.server.setPublicationStrategy(
|
||||
"foo",
|
||||
DDPServer.publicationStrategies.NO_MERGE
|
||||
);
|
||||
```
|
||||
|
||||
<ApiBox name="getPublicationStrategy" />
|
||||
|
||||
### Server connections {#connections}
|
||||
|
||||
Functions to manage and inspect the network connection between the Meteor client and server.
|
||||
|
||||
<ApiBox name="Meteor.status" hasCustomExample/>
|
||||
|
||||
```js
|
||||
import { Meteor } from "meteor/meteor";
|
||||
const status = Meteor.status();
|
||||
|
||||
console.log(status);
|
||||
// ^^^^
|
||||
// {
|
||||
// connected: Boolean,
|
||||
// status: String,
|
||||
// retryCount: Number,
|
||||
// retryTime: Number,
|
||||
// reason: String,
|
||||
// }
|
||||
```
|
||||
|
||||
Status object has the following fields:
|
||||
|
||||
- `connected` - _*Boolean*_ : True if currently connected to the server. If false, changes and
|
||||
method invocations will be queued up until the connection is reestablished.
|
||||
- `status` - _*String*_: Describes the current reconnection status. The possible
|
||||
values are `connected` (the connection is up and
|
||||
running), `connecting` (disconnected and trying to open a
|
||||
new connection), `failed` (permanently failed to connect; e.g., the client
|
||||
and server support different versions of DDP), `waiting` (failed
|
||||
to connect and waiting to try to reconnect) and `offline` (user has disconnected the connection).
|
||||
- `retryCount` - _*Number*_: The number of times the client has tried to reconnect since the
|
||||
connection was lost. 0 when connected.
|
||||
- `retryTime` - _*Number or undefined*_: The estimated time of the next reconnection attempt. To turn this
|
||||
into an interval until the next reconnection, This key will be set only when `status` is `waiting`.
|
||||
You canuse this snippet:
|
||||
```js
|
||||
retryTime - new Date().getTime();
|
||||
```
|
||||
- `reason` - _*String or undefined*_: If `status` is `failed`, a description of why the connection failed.
|
||||
|
||||
<ApiBox name="Meteor.reconnect" />
|
||||
|
||||
<ApiBox name="Meteor.disconnect" />
|
||||
|
||||
Call this method to disconnect from the server and stop all
|
||||
live data updates. While the client is disconnected it will not receive
|
||||
updates to collections, method calls will be queued until the
|
||||
connection is reestablished, and hot code push will be disabled.
|
||||
|
||||
Call [Meteor.reconnect](#Meteor-reconnect) to reestablish the connection
|
||||
and resume data transfer.
|
||||
|
||||
This can be used to save battery on mobile devices when real time
|
||||
updates are not required.
|
||||
|
||||
<ApiBox name="Meteor.onConnection" hasCustomExample/>
|
||||
|
||||
```js
|
||||
import { Meteor } from "meteor/meteor";
|
||||
|
||||
const handle = Meteor.onConnection((connection) => {
|
||||
console.log(connection);
|
||||
// ^^^^^^^^^^^
|
||||
// {
|
||||
// id: String,
|
||||
// close: Function,
|
||||
// onClose: Function,
|
||||
// clientAddress: String,
|
||||
// httpHeaders: Object,
|
||||
// }
|
||||
});
|
||||
|
||||
handle.stop(); // Unregister the callback
|
||||
```
|
||||
|
||||
`onConnection` returns an object with a single method `stop`. Calling
|
||||
`stop` unregisters the callback, so that this callback will no longer
|
||||
be called on new connections.
|
||||
|
||||
The callback is called with a single argument, the server-side
|
||||
`connection` representing the connection from the client. This object
|
||||
contains the following fields:
|
||||
|
||||
- `id` - _*String*_: A globally unique id for this connection.
|
||||
- `close` - _*Function*_: Close this DDP connection. The client is free to reconnect, but will
|
||||
receive a different connection with a new `id` if it does.
|
||||
- `onClose` - _*Function*_: Register a callback to be called when the connection is closed.
|
||||
If the connection is already closed, the callback will be called immediately.
|
||||
- `clientAddress` - _*String*_: The IP address of the client in dotted form (such as `127.0.0.1`). If you're running your Meteor server behind a proxy (so that clients
|
||||
are connecting to the proxy instead of to your server directly),
|
||||
you'll need to set the `HTTP_FORWARDED_COUNT` environment variable
|
||||
for the correct IP address to be reported by `clientAddress`.
|
||||
|
||||
Set `HTTP_FORWARDED_COUNT` to an integer representing the number of
|
||||
proxies in front of your server. For example, you'd set it to `1`
|
||||
when your server was behind one proxy.
|
||||
|
||||
- `httpHeaders` - _*Object*_: When the connection came in over an HTTP transport (such as with
|
||||
Meteor's default SockJS implementation), this field contains
|
||||
whitelisted HTTP headers.
|
||||
|
||||
Cookies are deliberately excluded from the headers as they are a
|
||||
security risk for this transport. For details and alternatives, see
|
||||
the [SockJS documentation](https://github.com/sockjs/sockjs-node#authorisation).
|
||||
|
||||
> Currently when a client reconnects to the server (such as after
|
||||
> temporarily losing its Internet connection), it will get a new
|
||||
> connection each time. The `onConnection` callbacks will be called
|
||||
> again, and the new connection will have a new connection `id`.
|
||||
|
||||
> In the future, when client reconnection is fully implemented,
|
||||
> reconnecting from the client will reconnect to the same connection on
|
||||
> the server: the `onConnection` callback won't be called for that
|
||||
> connection again, and the connection will still have the same
|
||||
> connection `id`.
|
||||
|
||||
<ApiBox name="DDP.connect" hasCustomExample/>
|
||||
|
||||
```js
|
||||
import { DDP } from "meteor/ddp-client";
|
||||
import { Mongo } from "meteor/mongo";
|
||||
import { Meteor } from "meteor/meteor";
|
||||
const options = {...};
|
||||
|
||||
const otherServer = DDP.connect("http://example.com", options);
|
||||
|
||||
otherServer.call("foo.from.other.server", 1, 2, function (err, result) {
|
||||
// ...
|
||||
});
|
||||
|
||||
Metepr.call("foo.from.this.server", 1, 2, function (err, result) {
|
||||
// ...
|
||||
});
|
||||
const remoteColl = new Mongo.Collection("collectionName", { connection: otherServer });
|
||||
remoteColl.find(...);
|
||||
|
||||
|
||||
```
|
||||
|
||||
To call methods on another Meteor application or subscribe to its data
|
||||
sets, call `DDP.connect` with the URL of the application.
|
||||
`DDP.connect` returns an object which provides:
|
||||
|
||||
- `subscribe` -
|
||||
Subscribe to a record set. See
|
||||
[Meteor.subscribe](#Meteor-subscribe).
|
||||
- `call` -
|
||||
Invoke a method. See [Meteor.call](#Meteor-call).
|
||||
- `apply` -
|
||||
Invoke a method with an argument array. See
|
||||
[Meteor.apply](#Meteor-apply).
|
||||
- `methods` -
|
||||
Define client-only stubs for methods defined on the remote server. See
|
||||
[Meteor.methods](#Meteor-methods).
|
||||
- `status` -
|
||||
Get the current connection status. See
|
||||
[Meteor.status](#Meteor-status).
|
||||
- `reconnect` -
|
||||
See [Meteor.reconnect](#Meteor-reconnect).
|
||||
- `disconnect` -
|
||||
See [Meteor.disconnect](#Meteor-disconnect).
|
||||
|
||||
By default, clients open a connection to the server from which they're loaded.
|
||||
When you call `Meteor.subscribe`, `Meteor.status`, `Meteor.call`, and
|
||||
`Meteor.apply`, you are using a connection back to that default
|
||||
server.
|
||||
|
||||
<ApiBox name="DDP.onReconnect" />
|
||||
|
||||
## Timers { #timers }
|
||||
|
||||
Meteor uses global environment variables
|
||||
to keep track of things like the current request's user. To make sure
|
||||
these variables have the right values, you need to use
|
||||
`Meteor.setTimeout` instead of `setTimeout` and `Meteor.setInterval`
|
||||
instead of `setInterval`.
|
||||
|
||||
These functions work just like their native JavaScript equivalents.
|
||||
If you call the native function, you'll get an error stating that Meteor
|
||||
code must always run within a Fiber, and advising to use
|
||||
`Meteor.bindEnvironment`.
|
||||
|
||||
<ApiBox name="Meteor.setTimeout" />
|
||||
|
||||
Returns a handle that can be used by `Meteor.clearTimeout`.
|
||||
|
||||
<ApiBox name="Meteor.setInterval" />
|
||||
|
||||
Returns a handle that can be used by `Meteor.clearInterval`.
|
||||
|
||||
<ApiBox name="Meteor.clearTimeout" />
|
||||
<ApiBox name="Meteor.clearInterval" />
|
||||
|
||||
## Enviroment variables {#envs}
|
||||
|
||||
teor runs most app code within Fibers, which allows keeping track of the context a function is running in. `Meteor.EnvironmentVariable` works with `Meteor.bindEnvironment`, promises, and many other Meteor API's to preserve the context in async code. Some examples of how it is used in Meteor are to store the current user in methods, and record which arguments have been checked when using `audit-argument-checks`.
|
||||
|
||||
```js
|
||||
import { Meteor } from "meteor/meteor";
|
||||
const currentRequest = new Meteor.EnvironmentVariable();
|
||||
|
||||
function log(message) {
|
||||
const requestId = currentRequest.get() || "None";
|
||||
console.log(`[${requestId}]`, message);
|
||||
}
|
||||
|
||||
currentRequest.withValue("12345", () => {
|
||||
log("Handling request"); // Logs: [12345] Handling request
|
||||
});
|
||||
```
|
||||
|
||||
<ApiBox name="Meteor.EnvironmentVariable" />
|
||||
<ApiBox name="Meteor.EnvironmentVariableAsync" />
|
||||
<ApiBox name="Meteor.EnvironmentVariable.get" />
|
||||
<ApiBox name="Meteor.EnvironmentVariable.withValue" />
|
||||
|
||||
<ApiBox name="Meteor.bindEnvironment" />
|
||||
482
v3-docs/docs/api/package.md
Normal file
482
v3-docs/docs/api/package.md
Normal file
@@ -0,0 +1,482 @@
|
||||
# Package.js
|
||||
|
||||
A package is a directory containing a package.js file, which
|
||||
contains roughly three major sections: a basic description, a package
|
||||
definition, and a test definition. By default, the directory name is the name of
|
||||
the package.
|
||||
|
||||
The `package.js` file below is an example of how to use the packaging API. The
|
||||
rest of this section will explain the specific API commands in greater detail.
|
||||
|
||||
```js
|
||||
// Information about this package:
|
||||
Package.describe({
|
||||
// Short two-sentence summary
|
||||
summary: 'What this does',
|
||||
// Version number
|
||||
version: '1.0.0',
|
||||
// Optional, default is package directory name
|
||||
name: 'username:package-name',
|
||||
// Optional GitHub URL to your source repository
|
||||
git: 'https://github.com/something/something.git'
|
||||
});
|
||||
|
||||
// This defines your actual package:
|
||||
Package.onUse((api) => {
|
||||
// If no version is specified for an `api.use` dependency, use the one defined
|
||||
// in Meteor 1.12.1.
|
||||
api.versionsFrom('1.12.1');
|
||||
// Use the `underscore` package, but only on the server. Version not
|
||||
// specified, so it will be as of Meteor 1.12.1.
|
||||
api.use('underscore', 'server');
|
||||
// Use `ostrio:flow-router-extra`, version 3.9.0 or newer.
|
||||
api.use('ostrio:flow-router-extra@3.9.0');
|
||||
// Give users of this package access to active-route's JavaScript helpers.
|
||||
api.imply('zimme:active-route@2.3.2')
|
||||
// Export the object `Email` to packages or apps that use this package.
|
||||
api.export('Email', 'server');
|
||||
// Specify the source code for the package.
|
||||
api.addFiles('email.js', 'server');
|
||||
// When using `ecmascript` or `modules` packages, you can use this instead of
|
||||
// `api.export` and `api.addFiles`.
|
||||
api.mainModule('email.js', 'server');
|
||||
});
|
||||
|
||||
// This defines the tests for the package:
|
||||
Package.onTest((api) => {
|
||||
// Sets up a dependency on this package.
|
||||
api.use('username:package-name');
|
||||
// Use the Mocha test framework.
|
||||
api.use('practicalmeteor:mocha@2.4.5_6');
|
||||
// Specify the source code for the package tests.
|
||||
api.addFiles('email_tests.js', 'server');
|
||||
});
|
||||
|
||||
// This lets you use npm packages in your package:
|
||||
Npm.depends({
|
||||
simplesmtp: '0.3.10',
|
||||
'stream-buffers': '0.2.5'
|
||||
});
|
||||
```
|
||||
|
||||
`api.mainModule` is documented in the [modules](../packages/modules.md#modular-application-structure) section.
|
||||
|
||||
Build plugins are created with
|
||||
[`Package.registerBuildPlugin`](#PackageNamespace-registerBuildPlugin). See the
|
||||
coffeescript package for an example. Build plugins are fully-fledged Meteor
|
||||
programs in their own right and have their own namespace, package dependencies,
|
||||
source files and npm requirements.
|
||||
|
||||
> You can use local packages to define custom build plugins
|
||||
for your app, with one caveat. In published packages, build plugins are already
|
||||
bundled with their transitive dependencies. So if you want a dependency of a
|
||||
build plugin to be satisfied by a local package, you must use a local copy of
|
||||
the package that defines the plugin (even if you make no changes to that
|
||||
package) so that Meteor will pick up the local dependency.
|
||||
|
||||
> In a lifecycle of a package there might come time to end the development for various reasons, or
|
||||
it gets superseded. In either case Meteor allows you to easily notify the users of the package by
|
||||
setting the deprecated flag to true: `deprecated: true` in the package description. In addition, you
|
||||
replace it with a string that tells the users where to find replacement or what to do.
|
||||
|
||||
Provide basic package information with `Package.describe(options)`. To publish a
|
||||
package, you must define `summary` and `version`.
|
||||
|
||||
<ApiBox name="PackageNamespace#describe" instanceName="api"/>
|
||||
|
||||
Define dependencies and expose package methods with the
|
||||
`Package.onUse` handler. This section lets you define what packages your package
|
||||
depends on, what packages are implied by your package, and what object your
|
||||
package is exported to.
|
||||
|
||||
<ApiBox name="PackageNamespace#onUse" instanceName="api"/>
|
||||
|
||||
<ApiBox name="PackageAPI#versionsFrom" instanceName="api"/>
|
||||
|
||||
> Choose Meteor versions carefully. First determine the minimum version of Meteor you need for the API you use in your package.
|
||||
This should be based on specific needs of your package like needed the *Async calls, which would require minimum version to be
|
||||
at least 2.8. Another example are where packages had a major version bump, for example this has happened with the accounts packages
|
||||
in Meteor 2.3. If you want to be backward and forward compatible it is good to include Meteor version before 2.3 and then 2.3.6 in the array.
|
||||
A general recommendation for most compatibility for accounts packages (unless you need API that was affected in Meteor 2.3) is to have the following
|
||||
array in `versionsFrom`: `['1.12.1', '2.3.6', '2.8.1']`, this gives us the widest range. For general packages you can leave out version `2.3.6`.
|
||||
If you want the widest compatibility range it is recommended that the lowest be `1.12.1` and that you also include another
|
||||
version near the current version of Meteor.
|
||||
|
||||
<ApiBox name="PackageAPI#use" instanceName="api"/>
|
||||
<ApiBox name="PackageAPI#imply" instanceName="api"/>
|
||||
<ApiBox name="PackageAPI#export" instanceName="api"/>
|
||||
<ApiBox name="PackageAPI#addFiles" instanceName="api"/>
|
||||
<ApiBox name="PackageAPI#addAssets" instanceName="api"/>
|
||||
|
||||
Set up your tests with the `Package.onTest` handler, which has an interface
|
||||
that's parallel to that of the `onUse` handler. The tests will need to depend on
|
||||
the package that you have just created. For example, if your package is the
|
||||
`email` package, you have to call `api.use('email')` in order to test the
|
||||
package.
|
||||
|
||||
If you used `meteor create` to set up your package, Meteor will create the
|
||||
required scaffolding in `package.js`, and you'll only need to add unit test code
|
||||
in the `_test.js` file that was created.
|
||||
|
||||
<ApiBox name="PackageNamespace#onTest" instanceName="api"/>
|
||||
|
||||
Meteor packages can include NPM packages and Cordova plugins by using
|
||||
`Npm.depends` and `Cordova.depends` in the `package.js` file.
|
||||
|
||||
<ApiBox name="PackageNpm#depends" instanceName="api"/>
|
||||
<ApiBox name="Npm.require" />
|
||||
<ApiBox name="PackageCordova#depends" instanceName="api"/>
|
||||
<ApiBox name="PackageNamespace#registerBuildPlugin" instanceName="api"/>
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
In some cases we need to offer options in packages where these options are not going to change in runtime.
|
||||
|
||||
We prefer to have these options defined in a configuration file instead of using JS code to call specific functions to define options in runtime.
|
||||
|
||||
For example, in `quave:collections` package you can force collections to be available only in the server like this:
|
||||
|
||||
```json
|
||||
"packages": {
|
||||
"quave:collections": {
|
||||
"isServerOnly": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We encourage every package author to follow this standard to offer options:
|
||||
|
||||
1. Use the official Meteor `settings` file
|
||||
2. Inside the `settings` file read from a `Meteor`.`packages`.`<package name>`.`<your option name>`
|
||||
> If it needs to be available in the client follow the same structure inside the `public` key.
|
||||
|
||||
You can use [quave:settings](https://github.com/quavedev/settings) package to read options in the format above already merging the private and public options.
|
||||
|
||||
This way we avoid having to call a specific code before another specific code in a package as the setting is stored in the settings, and the package can load it when necessary instead of relying on a specific order of calls from the developer in the app code.
|
||||
|
||||
> We've started to adopt this standard also in core packages on Meteor 1.10.2.
|
||||
|
||||
<ApiBox name="Plugin.registerSourceHandler" true />
|
||||
|
||||
## Build Plugins API {#build-plugin-api}
|
||||
|
||||
Meteor packages can provide build plugins - programs that integrate with the
|
||||
build tool Isobuild used to compile and bundle your application.
|
||||
|
||||
Starting with Meteor 1.2, the API used to plug into the build process is called
|
||||
"Build Plugins". There are 3 phases when a package's plugin can run: linting,
|
||||
compilation and minification. Here is an overview of operations Isobuild
|
||||
performs on the application and packages source:
|
||||
|
||||
1. Gather source files from the app folder or read `package.js` file for a
|
||||
package.
|
||||
2. Lint all source files and print the linting warnings.
|
||||
3. Compile the source files like CoffeeScript, ES2015, Less, or Templates to plain
|
||||
JavaScript and CSS.
|
||||
4. Link the JavaScript files: wrap them into closures and provide necessary
|
||||
package imports.
|
||||
5. Minify JavaScript and CSS files. Can also include concatenation of all files.
|
||||
|
||||
Build plugins fill the phases 2, 3 and 5.
|
||||
|
||||
Usually build plugins implement a class that is given a list of files to
|
||||
process. Commonly, such files have the following methods:
|
||||
|
||||
- `getContentsAsBuffer` - Returns the full contents of the file as a buffer.
|
||||
- `getContentsAsString` - Returns the full contents of the file as a string.
|
||||
- `getPackageName` - Returns the name of the package or `null` if the file is not in a package.
|
||||
- `getPathInPackage` - Returns the relative path of file to the package or app root directory. The returned path always uses forward slashes.
|
||||
- `getSourceHash` - Returns a hash string for the file that can be used to implement caching.
|
||||
- `getArch` - Returns the architecture that is targeted while processing this file.
|
||||
- `getBasename` - Returns the filename of the file.
|
||||
- `getDirname` - Returns the directory path relative to the package or app root. The returned path always uses forward slashes.
|
||||
- `error` - Call this method to raise a compilation or linting error for the file.
|
||||
|
||||
|
||||
## Linters {#build-plugin-linters}
|
||||
|
||||
Linters are programs that check the code for undeclared variables or find code
|
||||
that doesn't correspond to certain style guidelines. Some of the popular
|
||||
examples of linters are [JSHint](http://jshint.com/about/) and
|
||||
[ESLint](http://eslint.org/). Some of the non-JavaScript linter examples include
|
||||
[CoffeeLint](https://github.com/clutchski/coffeelint) for CoffeeScript and
|
||||
[CSSLint](http://csslint.net/) for CSS.
|
||||
|
||||
To register a linter build plugin in your package, you need to do a couple of
|
||||
things in your `package.js`:
|
||||
- depend on the `isobuild:linter-plugin@1.0.0` package
|
||||
- register a build plugin: `Package.registerBuildPlugin({ name, sources, ... });`
|
||||
(see [docs](#PackageNamespace-registerBuildPlugin))
|
||||
|
||||
In your build plugin sources, register a Linter Plugin: provide details such as
|
||||
a name, list of extensions and filenames the plugin will handle and a factory
|
||||
function that returns an instance of a linter class. Example:
|
||||
|
||||
```js
|
||||
Plugin.registerLinter({
|
||||
extensions: ['js'],
|
||||
filenames: ['.linterrc']
|
||||
}, () => new MyLinter);
|
||||
```
|
||||
|
||||
In this example, we register a linter that runs on all `js` files and also reads
|
||||
a file named `.linterrc` to get a configuration.
|
||||
|
||||
The `MyLinter` class should now implement the `processFilesForPackage`
|
||||
method. The method should accept two arguments: a list of files and an options
|
||||
object.
|
||||
|
||||
```js
|
||||
class MyLinter {
|
||||
processFilesForPackage(files, options) {
|
||||
files.forEach((file) => {
|
||||
// Lint the file.
|
||||
const lint = lintFile(file.getContentsAsString());
|
||||
|
||||
if (lint) {
|
||||
// If there are linting errors, output them.
|
||||
const { message, line, column } = lint;
|
||||
file.error({ message, line, column });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The globals are passed in the options object so that the linters can omit the
|
||||
warnings about the package imports that look like global variables.
|
||||
|
||||
Each file in the list is an object that has all the methods provided by all
|
||||
build plugins, described above.
|
||||
|
||||
See an example of a linting plugin implemented in Core: [jshint](https://github.com/meteor/meteor/tree/devel/packages/jshint).
|
||||
|
||||
|
||||
## Compilers {#build-plugin-compilers}
|
||||
|
||||
Compilers are programs that take the source files and output JavaScript or
|
||||
CSS. They also can output parts of HTML that is added to the `<head>` tag and
|
||||
static assets. Examples for the compiler plugins are: CoffeeScript, Babel.js,
|
||||
JSX compilers, Pug templating compiler and others.
|
||||
|
||||
To register a compiler plugin in your package, you need to do the following in
|
||||
your `package.js` file:
|
||||
- depend on the `isobuild:compiler-plugin@1.0.0` package
|
||||
- register a build plugin: `Package.registerBuildPlugin({ name, sources, ... });`
|
||||
(see [docs](#PackageNamespace-registerBuildPlugin))
|
||||
|
||||
In your build plugin source, register a Compiler Plugin: similar to other types
|
||||
of build plugins, provide the details, extensions and filenames and a factory
|
||||
function that returns an instance of the compiler. Ex.:
|
||||
|
||||
```js
|
||||
Plugin.registerCompiler({
|
||||
extensions: ['pug', 'tpl.pug'],
|
||||
filenames: []
|
||||
}, () => new PugCompiler);
|
||||
```
|
||||
|
||||
The compiler class must implement the `processFilesForTarget` method that is
|
||||
given the source files for a target (server or client part of the package/app).
|
||||
|
||||
```js
|
||||
class PugCompiler {
|
||||
processFilesForTarget(files) {
|
||||
files.forEach((file) => {
|
||||
// Process and add the output.
|
||||
const output = compilePug(file.getContentsAsString());
|
||||
|
||||
file.addJavaScript({
|
||||
data: output,
|
||||
path: `${file.getPathInPackage()}.js`
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Besides the common methods available on the input files' class, the following
|
||||
methods are available:
|
||||
|
||||
- `getExtension` - Returns the extension that matched the compiler plugin. The
|
||||
longest prefix is preferred.
|
||||
- `getDeclaredExports` - Returns a list of symbols declared as exports in this
|
||||
target. The result of `api.export('symbol')` calls in target's control file
|
||||
such as package.js.
|
||||
- `getDisplayPath` Returns a relative path that can be used to form error
|
||||
messages or other display properties. Can be used as an input to a source map.
|
||||
- `addStylesheet` - Web targets only. Add a stylesheet to the document. Not
|
||||
available for linter build plugins.
|
||||
- `addJavaScript` - Add JavaScript code. The code added will only see the
|
||||
namespaces imported by this package as runtime dependencies using
|
||||
['api.use'](#PackageAPI-use). If the file being compiled was added with the
|
||||
bare flag, the resulting JavaScript won't be wrapped in a closure.
|
||||
- `addAsset` - Add a file to serve as-is to the browser or to include on the
|
||||
browser, depending on the target. On the web, it will be served at the exact
|
||||
path requested. For server targets, it can be retrieved using
|
||||
`Assets.getText` or `Assets.getBinary`.
|
||||
- `addHtml` - Works in web targets only. Add markup to the `head` or `body`
|
||||
section of the document.
|
||||
- `hmrAvailable` - Returns true if the file can be updated with HMR. Among other things,
|
||||
it checks if HMR supports the current architecture and build mode, and that the unibuild
|
||||
uses the `hot-module-replacement` package. There are rare situations where `hmrAvailable`
|
||||
returns true, but when more information is available later in the build process Meteor
|
||||
decides the file can not be updated with HMR.
|
||||
- `readAndWatchFileWithHash` - Accepts an absolute path, and returns { contents, hash }
|
||||
Makes sure Meteor watches the file so any changes to it will trigger a rebuild
|
||||
|
||||
Meteor implements a couple of compilers as Core packages, good examples would be
|
||||
the
|
||||
[Blaze templating](https://github.com/meteor/meteor/tree/devel/packages/templating)
|
||||
package and the
|
||||
[ecmascript](https://github.com/meteor/meteor/tree/devel/packages/ecmascript)
|
||||
package (compiles ES2015+ to JavaScript that can run in the browsers).
|
||||
|
||||
## Minifiers {#build-plugin-minifiers}
|
||||
|
||||
Minifiers run last after the sources has been compiled and JavaScript code has
|
||||
been linked. Minifiers are only ran for the client programs (`web.browser` and
|
||||
`web.cordova`).
|
||||
|
||||
There are two types of minifiers one can add: a minifier processing JavaScript
|
||||
(registered extensions: `['js']`) and a minifier processing CSS (extensions:
|
||||
`['css']`).
|
||||
|
||||
To register a minifier plugin in your package, add the following in your
|
||||
`package.js` file:
|
||||
- depend on `isobuild:minifier-plugin@1.0.0` package
|
||||
- register a build plugin: `Package.registerBuildPlugin({ name, sources, ... });`
|
||||
(see [docs](#PackageNamespace-registerBuildPlugin))
|
||||
|
||||
In your build plugin source, register a Minifier Plugin. Similar to Linter and
|
||||
Compiler plugin, specify the interested extensions (`css` or `js`). The factory
|
||||
function returns an instance of the minifier class.
|
||||
|
||||
```js
|
||||
Plugin.registerMinifier({
|
||||
extensions: ['js']
|
||||
}, () => new UglifyJsMinifier);
|
||||
```
|
||||
|
||||
The minifier class must implement the method `processFilesForBundle`. The first
|
||||
argument is a list of processed files and the options object specifies if the
|
||||
minifier is ran in production mode or development mode.
|
||||
|
||||
::: info
|
||||
This method can be asynchronous. If it returns a Promise, the build process will
|
||||
wait for it to resolve before continuing.
|
||||
:::
|
||||
|
||||
```js
|
||||
class UglifyJsMinifier {
|
||||
processFilesForBundle(files, options) {
|
||||
const { minifyMode } = options;
|
||||
|
||||
if (minifyMode === 'development') {
|
||||
// Don't minify in development.
|
||||
file.forEach((file) => {
|
||||
file.addJavaScript({
|
||||
data: file.getContentsAsBuffer(),
|
||||
sourceMap: file.getSourceMap(),
|
||||
path: file.getPathInBundle()
|
||||
});
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Minify in production.
|
||||
files.forEach((file) => {
|
||||
file.addJavaScript({
|
||||
data: uglifyjs.minify(file.getContentsAsBuffer()),
|
||||
path: file.getPathInBundle()
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, we re-add the same files in the development mode to avoid
|
||||
unnecessary work and then we minify the files in production mode.
|
||||
|
||||
Besides the common input files' methods, these methods are available:
|
||||
- `getPathInBundle` - returns a path of the processed file in the bundle.
|
||||
- `getSourcePath` - returns absolute path of the input file if available, or null.
|
||||
- `getSourceMap` - returns the source-map for the processed file if there is such.
|
||||
- `addJavaScript` - same as compilers
|
||||
- `addStylesheet` - same as compilers
|
||||
- `readAndWatchFileWithHash` - only available for css minifiers. Same as compilers.
|
||||
|
||||
Right now, Meteor Core ships with the `standard-minifiers` package that can be
|
||||
replaced with a custom one. The
|
||||
[source](https://github.com/meteor/meteor/tree/devel/packages/standard-minifiers)
|
||||
of the package is a good example how to build your own minification plugin.
|
||||
|
||||
In development builds, minifiers must meet these requirements to not prevent hot module replacement:
|
||||
|
||||
- Call `addJavasScript` once for each file to add the file's contents
|
||||
- The contents of the files are not modified
|
||||
|
||||
In the future Meteor will allow minifiers to concatenate or modify files in development without affected hot module replacement.
|
||||
|
||||
|
||||
## Caching {#build-plugin-caching}
|
||||
|
||||
Since the API allows build plugins to process multiple files at once, we encourage package authors to implement at least some in-memory caching for their plugins. Using the `getSourceHash` function for linters and compilers will allow quick incremental recompilations if the file is not reprocessed even when the contents didn't change.
|
||||
|
||||
For the fast rebuilds between the Isobuild process runs, plugins can implement on-disk caching. If a plugin implements the `setDiskCacheDirectory` method, it will be called from time to time with a new path on disk where the plugin can write its offline cache. The folder is correctly reset when the plugin is rebuilt or cache should be invalidated for any reason (for example, picked package versions set has changed).
|
||||
|
||||
### Caching Compiler {#build-plugin-caching-compiler}
|
||||
|
||||
There is a core package called `caching-compiler` that implements most of the common logic of keeping both in-memory and on-disk caches. The easiest way to implement caching correctly is to subclass the `CachingCompiler` or `MultiFileCachingCompiler` class from this package in your build plugin. `CachingCompiler` is for compilers that consider each file completely independently; `MultiFileCachingCompiler` is for compilers that allow files to reference each other. To get this class in your plugin namespace, add a dependency to the plugin definition:
|
||||
|
||||
```js
|
||||
Package.registerBuildPlugin({
|
||||
name: 'compileGG',
|
||||
use: ['caching-compiler@1.0.0'],
|
||||
sources: ['plugin/compile-gg.js']
|
||||
});
|
||||
```
|
||||
|
||||
## Accessing File System {#build-plugin-file-system}
|
||||
|
||||
Since the build plugins run as part of the Meteor tool, they follow the same file-system access convention - all file system paths always look like a Unix path: using forward slashes and having a root at '/', even on Windows. For example: paths `/usr/bin/program` and `/C/Program Files/Program/program.exe` are valid paths, and `C:\Program Files\Program\program.exe` is not.
|
||||
|
||||
So whenever you get a path in your build plugin implementation, via `getPathInPackage` or in an argument of the `setDiskCacheDirectory` method, the path will be a Unix path.
|
||||
|
||||
Now, on running on Windows, the usual node modules `fs` and `path` expect to get a DOS path. To assist you to write correct code, the `Plugin` symbol provides its own versions of `fs` and `path` that you can use instead (note that all methods on `fs` are fiberized and sync versions prefer using Fibers rather than freezing the whole event loop).
|
||||
|
||||
Also `Plugin` provides helper functions `convertToStandardPath` and `convertToOSPath` to convert to a Unix path or to the path expected by the node libraries regardless of the path origin.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
// On Windows
|
||||
const fs = Plugin.fs;
|
||||
const path = Plugin.path;
|
||||
|
||||
const filePath = path.join('/C/Program Files', 'Program/file.txt');
|
||||
console.log(filePath); // Prints '/C/Program Files/Program/file.txt'
|
||||
|
||||
fs.writeFileSync(filePath, 'Hello.'); // Writes to 'C:\Program Files\Program\file.txt'
|
||||
|
||||
console.log(Plugin.convertToOsPath(filePath)); // Prints 'C:\Program Files\Program\file.txt'
|
||||
```
|
||||
|
||||
|
||||
## Isobuild Feature Packages {#isobuild-features}
|
||||
|
||||
Starting with Meteor 1.2, packages can declare that they need a version of the Meteor tool whose Isobuild build system supports a certain feature. For example, packages must write `api.use('isobuild:compiler-plugin@1.0.0')` in order to call `Plugin.registerCompiler`. This means that a package can transition from the old `registerSourceHandler` API to `registerCompiler` and Version Solver will properly prevent the `registerCompiler` version from being chosen by older tools that don't know how to handle it.
|
||||
|
||||
This is the known Isobuild feature "packages" sorted by the first release of Meteor which supports them.
|
||||
|
||||
### Introduced in Meteor 1.2s {#isobuild-features-1.2}
|
||||
|
||||
- `compiler-plugin@1.0.0`: Allows use of `Plugin.registerCompiler`.
|
||||
- `linter-plugin@1.0.0`: Allows use of `Plugin.registerLinter`.
|
||||
- `minifier-plugin@1.0.0`: Allows use of `Plugin.registerMinifier`.
|
||||
- `isopack-2@1.0.0`: This package is published only in `isopack-2` format and won't work in versions
|
||||
of Meteor that don't support that format.
|
||||
- `prod-only@1.0.0`: Allows use of the `prodOnly` flag in `Package.describe`.
|
||||
- `isobuild:cordova@5.4.0`: This package depends on a specific version of Cordova, most likely as a result of the Cordova plugins it depends on.
|
||||
152
v3-docs/docs/api/packages-listing.md
Normal file
152
v3-docs/docs/api/packages-listing.md
Normal file
@@ -0,0 +1,152 @@
|
||||
|
||||
|
||||
[//]: # (Do not edit this file by hand.)
|
||||
|
||||
[//]: # (This is a generated file.)
|
||||
|
||||
[//]: # (If you want to change something in this file)
|
||||
|
||||
[//]: # (go to meteor/docs/generators/packages-listing)
|
||||
|
||||
# Core Packages
|
||||
|
||||
|
||||
### [blaze](https://github.com/meteor/blaze) {#blaze}
|
||||
### [react-packages](https://github.com/meteor/react-packages) {#react-packages}
|
||||
### [accounts-2fa](https://github.com/meteor/meteor/tree/devel/packages/accounts-2fa) {#accounts-2fa}
|
||||
### [accounts-base](https://github.com/meteor/meteor/tree/devel/packages/accounts-base) {#accounts-base}
|
||||
### [accounts-facebook](https://github.com/meteor/meteor/tree/devel/packages/accounts-facebook) {#accounts-facebook}
|
||||
### [accounts-github](https://github.com/meteor/meteor/tree/devel/packages/accounts-github) {#accounts-github}
|
||||
### [accounts-google](https://github.com/meteor/meteor/tree/devel/packages/accounts-google) {#accounts-google}
|
||||
### [accounts-meetup](https://github.com/meteor/meteor/tree/devel/packages/accounts-meetup) {#accounts-meetup}
|
||||
### [accounts-meteor-developer](https://github.com/meteor/meteor/tree/devel/packages/accounts-meteor-developer) {#accounts-meteor-developer}
|
||||
### [accounts-oauth](https://github.com/meteor/meteor/tree/devel/packages/accounts-oauth) {#accounts-oauth}
|
||||
### [accounts-password](https://github.com/meteor/meteor/tree/devel/packages/accounts-password) {#accounts-password}
|
||||
### [accounts-passwordless](https://github.com/meteor/meteor/tree/devel/packages/accounts-passwordless) {#accounts-passwordless}
|
||||
### [accounts-twitter](https://github.com/meteor/meteor/tree/devel/packages/accounts-twitter) {#accounts-twitter}
|
||||
### [accounts-ui](https://github.com/meteor/meteor/tree/devel/packages/accounts-ui) {#accounts-ui}
|
||||
### [accounts-ui-unstyled](https://github.com/meteor/meteor/tree/devel/packages/accounts-ui-unstyled) {#accounts-ui-unstyled}
|
||||
### [accounts-weibo](https://github.com/meteor/meteor/tree/devel/packages/accounts-weibo) {#accounts-weibo}
|
||||
### [allow-deny](https://github.com/meteor/meteor/tree/devel/packages/allow-deny) {#allow-deny}
|
||||
### [audit-argument-checks](https://github.com/meteor/meteor/tree/devel/packages/audit-argument-checks) {#audit-argument-checks}
|
||||
### [autopublish](https://github.com/meteor/meteor/tree/devel/packages/autopublish) {#autopublish}
|
||||
### [autoupdate](https://github.com/meteor/meteor/tree/devel/packages/autoupdate) {#autoupdate}
|
||||
### [babel-compiler](https://github.com/meteor/meteor/tree/devel/packages/babel-compiler) {#babel-compiler}
|
||||
### [babel-runtime](https://github.com/meteor/meteor/tree/devel/packages/babel-runtime) {#babel-runtime}
|
||||
### [base64](https://github.com/meteor/meteor/tree/devel/packages/base64) {#base64}
|
||||
### [binary-heap](https://github.com/meteor/meteor/tree/devel/packages/binary-heap) {#binary-heap}
|
||||
### [boilerplate-generator](https://github.com/meteor/meteor/tree/devel/packages/boilerplate-generator) {#boilerplate-generator}
|
||||
### [boilerplate-generator-tests](https://github.com/meteor/meteor/tree/devel/packages/boilerplate-generator-tests) {#boilerplate-generator-tests}
|
||||
### [browser-policy](https://github.com/meteor/meteor/tree/devel/packages/browser-policy) {#browser-policy}
|
||||
### [browser-policy-common](https://github.com/meteor/meteor/tree/devel/packages/browser-policy-common) {#browser-policy-common}
|
||||
### [browser-policy-content](https://github.com/meteor/meteor/tree/devel/packages/browser-policy-content) {#browser-policy-content}
|
||||
### [browser-policy-framing](https://github.com/meteor/meteor/tree/devel/packages/browser-policy-framing) {#browser-policy-framing}
|
||||
### [caching-compiler](https://github.com/meteor/meteor/tree/devel/packages/caching-compiler) {#caching-compiler}
|
||||
### [callback-hook](https://github.com/meteor/meteor/tree/devel/packages/callback-hook) {#callback-hook}
|
||||
### [check](https://github.com/meteor/meteor/tree/devel/packages/check) {#check}
|
||||
### [constraint-solver](https://github.com/meteor/meteor/tree/devel/packages/constraint-solver) {#constraint-solver}
|
||||
### [core-runtime](https://github.com/meteor/meteor/tree/devel/packages/core-runtime) {#core-runtime}
|
||||
### [crosswalk](https://github.com/meteor/meteor/tree/devel/packages/crosswalk) {#crosswalk}
|
||||
### [ddp](https://github.com/meteor/meteor/tree/devel/packages/ddp) {#ddp}
|
||||
### [ddp-client](https://github.com/meteor/meteor/tree/devel/packages/ddp-client) {#ddp-client}
|
||||
### [ddp-common](https://github.com/meteor/meteor/tree/devel/packages/ddp-common) {#ddp-common}
|
||||
### [ddp-rate-limiter](https://github.com/meteor/meteor/tree/devel/packages/ddp-rate-limiter) {#ddp-rate-limiter}
|
||||
### [ddp-server](https://github.com/meteor/meteor/tree/devel/packages/ddp-server) {#ddp-server}
|
||||
### [deprecated](https://github.com/meteor/meteor/tree/devel/packages/deprecated) {#deprecated}
|
||||
### [dev-error-overlay](https://github.com/meteor/meteor/tree/devel/packages/dev-error-overlay) {#dev-error-overlay}
|
||||
### [diff-sequence](https://github.com/meteor/meteor/tree/devel/packages/diff-sequence) {#diff-sequence}
|
||||
### [disable-oplog](https://github.com/meteor/meteor/tree/devel/packages/disable-oplog) {#disable-oplog}
|
||||
### [dynamic-import](https://github.com/meteor/meteor/tree/devel/packages/dynamic-import) {#dynamic-import}
|
||||
### [ecmascript](https://github.com/meteor/meteor/tree/devel/packages/ecmascript) {#ecmascript}
|
||||
### [ecmascript-runtime](https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime) {#ecmascript-runtime}
|
||||
### [ecmascript-runtime-client](https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime-client) {#ecmascript-runtime-client}
|
||||
### [ecmascript-runtime-server](https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime-server) {#ecmascript-runtime-server}
|
||||
### [ejson](https://github.com/meteor/meteor/tree/devel/packages/ejson) {#ejson}
|
||||
### [email](https://github.com/meteor/meteor/tree/devel/packages/email) {#email}
|
||||
### [es5-shim](https://github.com/meteor/meteor/tree/devel/packages/es5-shim) {#es5-shim}
|
||||
### [facebook-config-ui](https://github.com/meteor/meteor/tree/devel/packages/facebook-config-ui) {#facebook-config-ui}
|
||||
### [facebook-oauth](https://github.com/meteor/meteor/tree/devel/packages/facebook-oauth) {#facebook-oauth}
|
||||
### [facts-base](https://github.com/meteor/meteor/tree/devel/packages/facts-base) {#facts-base}
|
||||
### [facts-ui](https://github.com/meteor/meteor/tree/devel/packages/facts-ui) {#facts-ui}
|
||||
### [fetch](https://github.com/meteor/meteor/tree/devel/packages/fetch) {#fetch}
|
||||
### [force-ssl](https://github.com/meteor/meteor/tree/devel/packages/force-ssl) {#force-ssl}
|
||||
### [force-ssl-common](https://github.com/meteor/meteor/tree/devel/packages/force-ssl-common) {#force-ssl-common}
|
||||
### [geojson-utils](https://github.com/meteor/meteor/tree/devel/packages/geojson-utils) {#geojson-utils}
|
||||
### [github-config-ui](https://github.com/meteor/meteor/tree/devel/packages/github-config-ui) {#github-config-ui}
|
||||
### [github-oauth](https://github.com/meteor/meteor/tree/devel/packages/github-oauth) {#github-oauth}
|
||||
### [google-config-ui](https://github.com/meteor/meteor/tree/devel/packages/google-config-ui) {#google-config-ui}
|
||||
### [google-oauth](https://github.com/meteor/meteor/tree/devel/packages/google-oauth) {#google-oauth}
|
||||
### [hot-code-push](https://github.com/meteor/meteor/tree/devel/packages/hot-code-push) {#hot-code-push}
|
||||
### [hot-module-replacement](https://github.com/meteor/meteor/tree/devel/packages/hot-module-replacement) {#hot-module-replacement}
|
||||
### [id-map](https://github.com/meteor/meteor/tree/devel/packages/id-map) {#id-map}
|
||||
### [insecure](https://github.com/meteor/meteor/tree/devel/packages/insecure) {#insecure}
|
||||
### [inter-process-messaging](https://github.com/meteor/meteor/tree/devel/packages/inter-process-messaging) {#inter-process-messaging}
|
||||
### [launch-screen](https://github.com/meteor/meteor/tree/devel/packages/launch-screen) {#launch-screen}
|
||||
### [localstorage](https://github.com/meteor/meteor/tree/devel/packages/localstorage) {#localstorage}
|
||||
### [logging](https://github.com/meteor/meteor/tree/devel/packages/logging) {#logging}
|
||||
### [logic-solver](https://github.com/meteor/meteor/tree/devel/packages/logic-solver) {#logic-solver}
|
||||
### [meetup-config-ui](https://github.com/meteor/meteor/tree/devel/packages/meetup-config-ui) {#meetup-config-ui}
|
||||
### [meetup-oauth](https://github.com/meteor/meteor/tree/devel/packages/meetup-oauth) {#meetup-oauth}
|
||||
### [meteor](https://github.com/meteor/meteor/tree/devel/packages/meteor) {#meteor}
|
||||
### [meteor-base](https://github.com/meteor/meteor/tree/devel/packages/meteor-base) {#meteor-base}
|
||||
### [meteor-developer-config-ui](https://github.com/meteor/meteor/tree/devel/packages/meteor-developer-config-ui) {#meteor-developer-config-ui}
|
||||
### [meteor-developer-oauth](https://github.com/meteor/meteor/tree/devel/packages/meteor-developer-oauth) {#meteor-developer-oauth}
|
||||
### [meteor-tool](https://github.com/meteor/meteor/tree/devel/packages/meteor-tool) {#meteor-tool}
|
||||
### [minifier-css](https://github.com/meteor/meteor/tree/devel/packages/minifier-css) {#minifier-css}
|
||||
### [minifier-js](https://github.com/meteor/meteor/tree/devel/packages/minifier-js) {#minifier-js}
|
||||
### [minimongo](https://github.com/meteor/meteor/tree/devel/packages/minimongo) {#minimongo}
|
||||
### [mobile-experience](https://github.com/meteor/meteor/tree/devel/packages/mobile-experience) {#mobile-experience}
|
||||
### [mobile-status-bar](https://github.com/meteor/meteor/tree/devel/packages/mobile-status-bar) {#mobile-status-bar}
|
||||
### [modern-browsers](https://github.com/meteor/meteor/tree/devel/packages/modern-browsers) {#modern-browsers}
|
||||
### [modules](https://github.com/meteor/meteor/tree/devel/packages/modules) {#modules}
|
||||
### [modules-runtime](https://github.com/meteor/meteor/tree/devel/packages/modules-runtime) {#modules-runtime}
|
||||
### [modules-runtime-hot](https://github.com/meteor/meteor/tree/devel/packages/modules-runtime-hot) {#modules-runtime-hot}
|
||||
### [mongo](https://github.com/meteor/meteor/tree/devel/packages/mongo) {#mongo}
|
||||
### [mongo-dev-server](https://github.com/meteor/meteor/tree/devel/packages/mongo-dev-server) {#mongo-dev-server}
|
||||
### [mongo-id](https://github.com/meteor/meteor/tree/devel/packages/mongo-id) {#mongo-id}
|
||||
### [mongo-livedata](https://github.com/meteor/meteor/tree/devel/packages/mongo-livedata) {#mongo-livedata}
|
||||
### [npm-mongo](https://github.com/meteor/meteor/tree/devel/packages/npm-mongo) {#npm-mongo}
|
||||
### [oauth](https://github.com/meteor/meteor/tree/devel/packages/oauth) {#oauth}
|
||||
### [oauth-encryption](https://github.com/meteor/meteor/tree/devel/packages/oauth-encryption) {#oauth-encryption}
|
||||
### [oauth1](https://github.com/meteor/meteor/tree/devel/packages/oauth1) {#oauth1}
|
||||
### [oauth2](https://github.com/meteor/meteor/tree/devel/packages/oauth2) {#oauth2}
|
||||
### [ordered-dict](https://github.com/meteor/meteor/tree/devel/packages/ordered-dict) {#ordered-dict}
|
||||
### [package-stats-opt-out](https://github.com/meteor/meteor/tree/devel/packages/package-stats-opt-out) {#package-stats-opt-out}
|
||||
### [package-version-parser](https://github.com/meteor/meteor/tree/devel/packages/package-version-parser) {#package-version-parser}
|
||||
### [promise](https://github.com/meteor/meteor/tree/devel/packages/promise) {#promise}
|
||||
### [random](https://github.com/meteor/meteor/tree/devel/packages/random) {#random}
|
||||
### [rate-limit](https://github.com/meteor/meteor/tree/devel/packages/rate-limit) {#rate-limit}
|
||||
### [react-fast-refresh](https://github.com/meteor/meteor/tree/devel/packages/react-fast-refresh) {#react-fast-refresh}
|
||||
### [reactive-dict](https://github.com/meteor/meteor/tree/devel/packages/reactive-dict) {#reactive-dict}
|
||||
### [reactive-var](https://github.com/meteor/meteor/tree/devel/packages/reactive-var) {#reactive-var}
|
||||
### [reload](https://github.com/meteor/meteor/tree/devel/packages/reload) {#reload}
|
||||
### [reload-safetybelt](https://github.com/meteor/meteor/tree/devel/packages/reload-safetybelt) {#reload-safetybelt}
|
||||
### [retry](https://github.com/meteor/meteor/tree/devel/packages/retry) {#retry}
|
||||
### [routepolicy](https://github.com/meteor/meteor/tree/devel/packages/routepolicy) {#routepolicy}
|
||||
### [server-render](https://github.com/meteor/meteor/tree/devel/packages/server-render) {#server-render}
|
||||
### [service-configuration](https://github.com/meteor/meteor/tree/devel/packages/service-configuration) {#service-configuration}
|
||||
### [session](https://github.com/meteor/meteor/tree/devel/packages/session) {#session}
|
||||
### [sha](https://github.com/meteor/meteor/tree/devel/packages/sha) {#sha}
|
||||
### [shell-server](https://github.com/meteor/meteor/tree/devel/packages/shell-server) {#shell-server}
|
||||
### [socket-stream-client](https://github.com/meteor/meteor/tree/devel/packages/socket-stream-client) {#socket-stream-client}
|
||||
### [standard-minifier-css](https://github.com/meteor/meteor/tree/devel/packages/standard-minifier-css) {#standard-minifier-css}
|
||||
### [standard-minifier-js](https://github.com/meteor/meteor/tree/devel/packages/standard-minifier-js) {#standard-minifier-js}
|
||||
### [standard-minifiers](https://github.com/meteor/meteor/tree/devel/packages/standard-minifiers) {#standard-minifiers}
|
||||
### [static-html](https://github.com/meteor/meteor/tree/devel/packages/static-html) {#static-html}
|
||||
### [test-helpers](https://github.com/meteor/meteor/tree/devel/packages/test-helpers) {#test-helpers}
|
||||
### [test-in-browser](https://github.com/meteor/meteor/tree/devel/packages/test-in-browser) {#test-in-browser}
|
||||
### [test-in-console](https://github.com/meteor/meteor/tree/devel/packages/test-in-console) {#test-in-console}
|
||||
### [test-server-tests-in-console-once](https://github.com/meteor/meteor/tree/devel/packages/test-server-tests-in-console-once) {#test-server-tests-in-console-once}
|
||||
### [tinytest](https://github.com/meteor/meteor/tree/devel/packages/tinytest) {#tinytest}
|
||||
### [tinytest-harness](https://github.com/meteor/meteor/tree/devel/packages/tinytest-harness) {#tinytest-harness}
|
||||
### [tracker](https://github.com/meteor/meteor/tree/devel/packages/tracker) {#tracker}
|
||||
### [twitter-config-ui](https://github.com/meteor/meteor/tree/devel/packages/twitter-config-ui) {#twitter-config-ui}
|
||||
### [twitter-oauth](https://github.com/meteor/meteor/tree/devel/packages/twitter-oauth) {#twitter-oauth}
|
||||
### [typescript](https://github.com/meteor/meteor/tree/devel/packages/typescript) {#typescript}
|
||||
### [underscore](https://github.com/meteor/meteor/tree/devel/packages/underscore) {#underscore}
|
||||
### [underscore-tests](https://github.com/meteor/meteor/tree/devel/packages/underscore-tests) {#underscore-tests}
|
||||
### [url](https://github.com/meteor/meteor/tree/devel/packages/url) {#url}
|
||||
### [webapp](https://github.com/meteor/meteor/tree/devel/packages/webapp) {#webapp}
|
||||
### [webapp-hashing](https://github.com/meteor/meteor/tree/devel/packages/webapp-hashing) {#webapp-hashing}
|
||||
### [weibo-config-ui](https://github.com/meteor/meteor/tree/devel/packages/weibo-config-ui) {#weibo-config-ui}
|
||||
### [weibo-oauth](https://github.com/meteor/meteor/tree/devel/packages/weibo-oauth) {#weibo-oauth}
|
||||
145
v3-docs/docs/api/session.md
Normal file
145
v3-docs/docs/api/session.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# Session
|
||||
|
||||
Documentation of Meteor's client-side session API.
|
||||
|
||||
|
||||
`Session` provides a global object on the client that you can use to
|
||||
store an arbitrary set of key-value pairs. Use it to store things like
|
||||
the currently selected item in a list.
|
||||
|
||||
What's special about `Session` is that it's reactive. If
|
||||
you call [`Session.get`](#Session-get)`('currentList')`
|
||||
from inside a template, the template will automatically be rerendered
|
||||
whenever [`Session.set`](#Session-set)`('currentList', x)` is called.
|
||||
|
||||
To add `Session` to your application, run this command in your terminal:
|
||||
|
||||
```bash
|
||||
meteor add session
|
||||
```
|
||||
|
||||
<ApiBox name="Session.set" hasCustomExample/>
|
||||
|
||||
|
||||
```js
|
||||
import { Session } from 'meteor/session';
|
||||
import { Tracker } from 'meteor/tracker';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
Tracker.autorun(() => {
|
||||
Meteor.subscribe('chatHistory', { room: Session.get('currentRoomId') });
|
||||
});
|
||||
|
||||
// Causes the function passed to `Tracker.autorun` to be rerun, so that the
|
||||
// 'chatHistory' subscription is moved to the room 'home'.
|
||||
Session.set('currentRoomId', 'home');
|
||||
```
|
||||
|
||||
|
||||
`Session.set` can also be called with an object of keys and values, which is
|
||||
equivalent to calling `Session.set` individually on each key/value pair.
|
||||
|
||||
```js
|
||||
Session.set({
|
||||
a: 'foo',
|
||||
b: 'bar'
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
<ApiBox name="Session.setDefault"/>
|
||||
|
||||
This is useful in initialization code, to avoid re-initializing a session
|
||||
variable every time a new version of your app is loaded.
|
||||
|
||||
|
||||
<ApiBox name="Session.get" hasCustomExample/>
|
||||
|
||||
This example in Blaze.js
|
||||
::: code-group
|
||||
|
||||
```html [main.html]
|
||||
<template name="main">
|
||||
<p>We've always been at war with {{theEnemy}}.</p>
|
||||
</template>
|
||||
```
|
||||
|
||||
```js [main.js]
|
||||
Template.main.helpers({
|
||||
theEnemy() {
|
||||
return Session.get('enemy');
|
||||
}
|
||||
});
|
||||
|
||||
Session.set('enemy', 'Eastasia');
|
||||
// Page will say "We've always been at war with Eastasia"
|
||||
|
||||
Session.set('enemy', 'Eurasia');
|
||||
// Page will change to say "We've always been at war with Eurasia"
|
||||
```
|
||||
:::
|
||||
|
||||
|
||||
<ApiBox name="Session.equals"/>
|
||||
|
||||
|
||||
If value is a scalar, then these two expressions do the same thing:
|
||||
|
||||
```js
|
||||
Session.get('key') === value
|
||||
Session.equals('key', value)
|
||||
```
|
||||
|
||||
...but the second one is always better. It triggers fewer invalidations
|
||||
(template redraws), making your program more efficient.
|
||||
|
||||
This example in Blaze.js:
|
||||
|
||||
::: code-group
|
||||
|
||||
```html [main.html]
|
||||
|
||||
<template name="postsView">
|
||||
{{#each posts}}
|
||||
{{> postItem}}
|
||||
{{/each}}
|
||||
</template>
|
||||
|
||||
<template name="postItem">
|
||||
<div class="{{postClass}}">{{title}}</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
```js [main.js]
|
||||
|
||||
Template.postsView.helpers({
|
||||
posts() {
|
||||
return Posts.find();
|
||||
}
|
||||
});
|
||||
|
||||
Template.postItem.helpers({
|
||||
postClass() {
|
||||
return Session.equals('selectedPost', this._id)
|
||||
? 'selected'
|
||||
: '';
|
||||
}
|
||||
});
|
||||
|
||||
Template.postItem.events({
|
||||
'click'() {
|
||||
Session.set('selectedPost', this._id);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Using Session.equals here means that when the user clicks
|
||||
on an item and changes the selection, only the newly selected
|
||||
and the newly unselected items are re-rendered.
|
||||
|
||||
If Session.get had been used instead of Session.equals, then
|
||||
when the selection changed, all the items would be re-rendered.
|
||||
|
||||
For object and array session values, you cannot use `Session.equals`; instead,
|
||||
you need to use the `underscore` package and write
|
||||
`_.isEqual(Session.get(key), value)`.
|
||||
5
v3-docs/docs/api/templates.md
Normal file
5
v3-docs/docs/api/templates.md
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
# Templates
|
||||
Documentation of Meteor's client-side session API.
|
||||
|
||||
This documentation has moved to the [Blaze Community Site](http://blazejs.org/api/templates).
|
||||
121
v3-docs/docs/cli/environment-variables.md
Normal file
121
v3-docs/docs/cli/environment-variables.md
Normal file
@@ -0,0 +1,121 @@
|
||||
|
||||
# Environment Variables
|
||||
List of environment variables that you can use with your Meteor application.
|
||||
|
||||
|
||||
## BIND_IP
|
||||
(_production_)
|
||||
|
||||
Bind the application server to a specific network interface by IP address, for example: `BIND_IP=192.168.0.2`.
|
||||
|
||||
See also: [`PORT`](#port).
|
||||
|
||||
> In development, this can be accomplished with `meteor run --port a.b.c.d:port`.
|
||||
|
||||
## DDP_DEFAULT_CONNECTION_URL
|
||||
(_develoment, production_)
|
||||
|
||||
There are some situations where it is valuable for the meteor client to use a different DDP server than the `ROOT_URL` server.
|
||||
|
||||
Setting `DDP_DEFAULT_CONNECTION_URL` when running a meteor server (development: `meteor run` or production: `node main.js`) will set the DDP server to the value in `DDP_DEFAULT_CONNECTION_URL`.
|
||||
|
||||
Setting `DDP_DEFAULT_CONNECTION_URL` when building (`meteor build`) will define the DDP server for `cordova` builds.
|
||||
|
||||
## DISABLE_WEBSOCKETS
|
||||
(_development, production_)
|
||||
|
||||
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.
|
||||
|
||||
## DISABLE_SOCKJS
|
||||
(_development, production_)
|
||||
|
||||
Set `DISABLE_SOCKJS=1` if you want to use the native WebSocket implementation instead of SockJS on the client side, for example, if you want to use a custom WebSocket implementation (e.g. [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js/)) on the server side.
|
||||
|
||||
## HTTP_FORWARDED_COUNT
|
||||
(_production_)
|
||||
|
||||
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.
|
||||
|
||||
## MAIL_URL
|
||||
(_development, production_)
|
||||
|
||||
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`.
|
||||
|
||||
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.
|
||||
|
||||
## METEOR_DISABLE_OPTIMISTIC_CACHING
|
||||
(_production_)
|
||||
|
||||
When running `meteor build` or `meteor deploy` you can set `METEOR_DISABLE_OPTIMISTIC_CACHING=1` to speed up your build time.
|
||||
|
||||
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.
|
||||
|
||||
## METEOR_PROFILE
|
||||
(_development_)
|
||||
|
||||
In development, you may need to diagnose what has made builds start taking a long time. To get the callstack and times during builds, you can run `METEOR_PROFILE=1 meteor`.
|
||||
|
||||
## METEOR_PACKAGE_DIRS
|
||||
(_development, production_)
|
||||
|
||||
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.
|
||||
|
||||
## METEOR_SETTINGS
|
||||
(_production_)
|
||||
|
||||
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" } }'`.
|
||||
|
||||
> 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](../api/meteor#Meteor-settings) documentation for further information.
|
||||
|
||||
## METEOR_SQLITE_JOURNAL_MODE
|
||||
(_development_)
|
||||
|
||||
The Meteor package catalog uses the `WAL` [SQLite Journal Mode](https://www.sqlite.org/pragma.html#pragma_journal_mode) by default. The Journal mode for the package catalog can be modifed by setting `METEOR_SQLITE_JOURNAL_MODE`.
|
||||
|
||||
When running multiple concurrent meteor servers on [Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/) some meteor developers have seen issues with the package catalog. Setting the environment variable `METEOR_SQLITE_JOURNAL_MODE=TRUNCATE` can overcome the issue.
|
||||
|
||||
## MONGO_OPLOG_URL
|
||||
(_development, production_)
|
||||
|
||||
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)"`
|
||||
|
||||
## MONGO_URL
|
||||
(_development, production_)
|
||||
|
||||
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/).
|
||||
|
||||
## PORT
|
||||
(_production_)
|
||||
|
||||
Which port the app should listen on, for example: `PORT=3030`
|
||||
|
||||
See also: [`BIND_IP`](#bind-ip).
|
||||
|
||||
> In development, this can be accomplished with `meteor run --port <port>`.
|
||||
|
||||
## ROOT_URL
|
||||
(_development, production_)
|
||||
|
||||
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"`.
|
||||
|
||||
## TOOL_NODE_FLAGS
|
||||
(_development, production_)
|
||||
|
||||
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"`
|
||||
For full list of available flags see the [Node documentation](https://nodejs.org/dist/latest-v12.x/docs/api/cli.html).
|
||||
|
||||
## UNIX_SOCKET_GROUP
|
||||
(_production_)
|
||||
|
||||
This overrides the default UNIX group of the socket file configured in `UNIX_SOCKET_PATH`. It can be set to a group name or a numerical gid.
|
||||
|
||||
## UNIX_SOCKET_PATH
|
||||
(_production_)
|
||||
|
||||
Configure Meteor's HTTP server to listen on a UNIX socket file path (e.g. `UNIX_SOCKET_PATH=/tmp/meteor.sock`) instead of a TCP port. This is useful when running a local reverse proxy server like Nginx to handle client HTTP requests and direct them to your Meteor application. Leveraging UNIX domain sockets for local communication on the same host avoids the Operating System overhead required by TCP based communication and can also improve security. This UNIX socket file is created when Meteor starts and removed when Meteor exits.
|
||||
|
||||
## UNIX_SOCKET_PERMISSIONS
|
||||
(_production_)
|
||||
|
||||
This overrides the default UNIX file permissions on the UNIX socket file configured in `UNIX_SOCKET_PATH`. For example, `UNIX_SOCKET_PERMISSIONS=660` would set read/write permissions for both the user and group.
|
||||
|
||||
1014
v3-docs/docs/cli/index.md
Normal file
1014
v3-docs/docs/cli/index.md
Normal file
File diff suppressed because it is too large
Load Diff
39
v3-docs/docs/cli/using-core-types.md
Normal file
39
v3-docs/docs/cli/using-core-types.md
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
# Using Core Types
|
||||
|
||||
Using core types with zodern:types
|
||||
|
||||
For MeteorJS in its version 2.8.1 we have introduced to our core packages an integration with the [zodern:types](https://github.com/zodern/meteor-types) package.
|
||||
This package allows you to use the TypeScript types for the Meteor core packages in your TypeScript code or JavaScript code.
|
||||
in order to use the types you need to install the package by running the command:
|
||||
|
||||
```bash
|
||||
meteor add zodern:types
|
||||
```
|
||||
|
||||
And add the following line to your `tsconfig.json` file (if you do not have one, create one and add the code bellow):
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"preserveSymlinks": true,
|
||||
"paths": {
|
||||
"meteor/*": [
|
||||
"node_modules/@types/meteor/*",
|
||||
".meteor/local/types/packages.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
then run the command:
|
||||
|
||||
```bash
|
||||
meteor lint
|
||||
```
|
||||
|
||||
this will create a file within your .meteor folder that will have your types for the core packages.
|
||||
You can continue to use your code as you did before, but now you can use the types for the core packages even if you are in JavaScript.
|
||||
|
||||
for more information about the package please visit the [zodern:types](https://github.com/zodern/meteor-types).
|
||||
136
v3-docs/docs/components/ApiBox.vue
Normal file
136
v3-docs/docs/components/ApiBox.vue
Normal file
@@ -0,0 +1,136 @@
|
||||
<script setup lang="ts">
|
||||
import Booleans from './helpers/Booleans.vue'
|
||||
import Classes from './helpers/Classes.vue'
|
||||
import Functions from './helpers/Functions.vue'
|
||||
import Locus from './helpers/Locus.vue'
|
||||
import ParamTable from './helpers/ParamTable.vue'
|
||||
import { useData } from 'vitepress'
|
||||
|
||||
import jsdoc from '../data/data.js'
|
||||
type Jsdoc = typeof jsdoc
|
||||
|
||||
type Params = keyof Jsdoc
|
||||
|
||||
const props = defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
hasCustomExample: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
instanceName: {
|
||||
type: String,
|
||||
default: 'this'
|
||||
},
|
||||
from: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
function getJsdoc<T extends Params>(key: T): Jsdoc[T] {
|
||||
if (!jsdoc[key]) {
|
||||
const { page } = useData()
|
||||
throw new Error(`
|
||||
jsdoc key: "${key}" not found.
|
||||
Refer to data/data.js for available keys.
|
||||
Error come from ${page.value.filePath}
|
||||
`)
|
||||
}
|
||||
|
||||
return jsdoc[key]
|
||||
}
|
||||
|
||||
const ui = getJsdoc(props.name)
|
||||
|
||||
if (props.from) {
|
||||
ui.module = props.from
|
||||
}
|
||||
const link = ui.longname.replace('.', '-').replace('#', '-')
|
||||
const isBoolean = ui.type?.names?.at(0) === 'Boolean'
|
||||
// if is constructor/class we change this to false;
|
||||
let isFunction = ui.kind === 'function' || ui?.params?.length > 0;
|
||||
|
||||
const isClass = (() => {
|
||||
if (ui.kind === 'class') {
|
||||
isFunction = false
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
|
||||
const isInstance = ui.scope === 'instance';
|
||||
const showName = (longname) => {
|
||||
if (isInstance) {
|
||||
// what is before # becomes `this`
|
||||
return longname.replace(/.*#/, `${props.instanceName}.`)
|
||||
}
|
||||
if (ui.ishelper) {
|
||||
return `{{ ${longname} }}`
|
||||
}
|
||||
|
||||
return longname
|
||||
}
|
||||
|
||||
if (isFunction) {
|
||||
for (const param of ui.params) {
|
||||
if (!param.type) {
|
||||
param.type = { names: [param.name] }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const gitHubSource = {
|
||||
lineNumber: ui.lineno,
|
||||
filePath: ui.filepath
|
||||
}
|
||||
|
||||
const debug = (name) => {
|
||||
if (ui.longname !== name) return
|
||||
console.log(ui)
|
||||
}
|
||||
// debug('Mongo.Collection')
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h2 :id="link">
|
||||
<a class="header-anchor" :href="'#' + link" :aria-label="'Permalink to "' + ui.longname + '"'"></a>
|
||||
|
||||
{{ showName(ui.longname) }}
|
||||
<Locus v-if="ui.locus && ui.locus !== 'Anywhere'" :locus="ui.locus" />
|
||||
|
||||
</h2>
|
||||
<h4>
|
||||
Summary:
|
||||
</h4>
|
||||
<div v-html="ui.summary"></div>
|
||||
<slot />
|
||||
<ParamTable v-if="isFunction || isClass" :params="ui.params" :options="ui.options" :from="gitHubSource" />
|
||||
<template v-if="!hasCustomExample">
|
||||
<Booleans v-if="isBoolean" :memberof="ui.memberof" :from="ui.module" :longname="ui.longname" />
|
||||
<Functions v-if="isFunction" :from="ui.module" :longname="ui.longname" :params="ui.params" :fnName="ui.name"
|
||||
:memberof="isInstance ? instanceName : ui.memberof" :scope="ui.scope" :returns="ui.returns" />
|
||||
<Classes v-if="isClass" :params="ui.params" :from="ui.module" :longname="ui.longname" />
|
||||
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
span {
|
||||
font-size: 0.8rem;
|
||||
color: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
h2 {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
69
v3-docs/docs/components/ApiMap.vue
Normal file
69
v3-docs/docs/components/ApiMap.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<script setup>
|
||||
import { useData } from "vitepress";
|
||||
import { ref, computed, watchEffect } from 'vue'
|
||||
import names from "../data/names.json";
|
||||
import { createMap, filterMap } from "./scripts/map-maker.js";
|
||||
|
||||
const apiList = createMap(names)
|
||||
const listRef = ref(apiList);
|
||||
const search = ref("");
|
||||
|
||||
watchEffect(() => {
|
||||
listRef.value = filterMap(search.value, apiList);
|
||||
})
|
||||
|
||||
const shouldRedirect = (list, api) => {
|
||||
if (list?.shouldGoTo) {
|
||||
return `${list.shouldGoTo}`;
|
||||
}
|
||||
return api;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
<label for="search">Search</label>
|
||||
<br />
|
||||
<input class="search" type="text" v-model="search" />
|
||||
<div v-for="(list, api, index) in listRef" :key="index">
|
||||
<h2 style="text-transform:capitalize;">
|
||||
<a :href="shouldRedirect(list, api)" style="text-decoration: none;">
|
||||
{{ api }}
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
<div v-for="(links, key) in list" :key="key">
|
||||
<!-- Should not render the shouldGoTo section -->
|
||||
<div v-if="key !== 'shouldGoTo'">
|
||||
<h5 v-for="(link) in links" :key="link">
|
||||
<a :href="shouldRedirect(list, api) + '#' + link.replace('#', '-').replace('.', '-')">
|
||||
{{ link }}
|
||||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.search {
|
||||
width: 100%;
|
||||
height: 2rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--vp-c-text-1);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--vp-c-brand-1);
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
||||
24
v3-docs/docs/components/helpers/Booleans.vue
Normal file
24
v3-docs/docs/components/helpers/Booleans.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
from: string;
|
||||
longname: string;
|
||||
memberof: string;
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="language-js vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">js</span>
|
||||
<pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { {{ props.memberof }} } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> 'meteor/{{ props.from }}'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">/** </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">@type</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> {Boolean}</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> */</span></span>
|
||||
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ({{ props.longname }}) {</span></span>
|
||||
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> // do something</span></span>
|
||||
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}</span></span></code></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
35
v3-docs/docs/components/helpers/Caret.vue
Normal file
35
v3-docs/docs/components/helpers/Caret.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
isOpen: boolean;
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="props.isOpen ? 'caret-btn-open ' : 'caret-btn-closed'" class="caret-btn " role="button"
|
||||
aria-label="toggle section" tabindex="0"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"
|
||||
viewBox="0 0 24 24" class="">
|
||||
<path
|
||||
d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.caret-btn {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
fill: currentColor;
|
||||
transition: transform 0.25s;
|
||||
display: inline-flex;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.caret-btn-closed {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
.caret-btn-open {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
</style>
|
||||
42
v3-docs/docs/components/helpers/Classes.vue
Normal file
42
v3-docs/docs/components/helpers/Classes.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
import { makePrimitiveHTML } from '../scripts/make-primitive-html';
|
||||
|
||||
const props = defineProps<{
|
||||
from: string;
|
||||
longname: string;
|
||||
params: {
|
||||
name: string;
|
||||
type: { names: string[] };
|
||||
description: string;
|
||||
optional?: boolean;
|
||||
}[];
|
||||
}>()
|
||||
|
||||
const toCamelCase = (str: string) => {
|
||||
if (str.includes(".")) {
|
||||
str = str.split(".")[1];
|
||||
}
|
||||
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) {
|
||||
return index === 0 ? word.toLowerCase() : word.toUpperCase();
|
||||
}).replace(/\s+/g, '');
|
||||
}
|
||||
|
||||
const isOneLiner = props?.params?.length === 0;
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="language-js vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">js</span> <pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { {{ props.longname.split(".")[0] }} } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> "meteor/{{ props.from }}""</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> {{ toCamelCase(props.longname) }}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> {{ props.longname }}</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(<span v-show="isOneLiner">);</span></span>
|
||||
<span class="line" v-for="(param, index) in props.params" :key="param.name"><span v-html="makePrimitiveHTML({ primitive: [param.type.names[0]], arr: props.params, index, isOptional: param.optional, name: param.name })"/></span>
|
||||
<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">);</span></span>
|
||||
</code></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
56
v3-docs/docs/components/helpers/Functions.vue
Normal file
56
v3-docs/docs/components/helpers/Functions.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
import { makePrimitiveHTML, comment, typeComment } from '../scripts/make-primitive-html';
|
||||
const props = defineProps<{
|
||||
from: string;
|
||||
longname: string;
|
||||
memberof: string;
|
||||
fnName: string;
|
||||
params: {
|
||||
name: string;
|
||||
type: { names: string[] };
|
||||
description: string;
|
||||
optional: boolean;
|
||||
}[];
|
||||
scope: string;
|
||||
returns?: {
|
||||
type: { names: string[] }
|
||||
}[]
|
||||
}>()
|
||||
const isOneLiner = props.params.length === 0;
|
||||
|
||||
function trimFnName(str: string) {
|
||||
if (str.startsWith('.')) {
|
||||
return str.slice(1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function getInstanceName(longname: string) {
|
||||
return longname.split("#")[0]
|
||||
}
|
||||
|
||||
|
||||
function getReturnType(returns: typeof props.returns) {
|
||||
const type = returns?.[0]?.type?.names[0];
|
||||
if (!type) return false;
|
||||
return type.replace('.<', '<');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="language-js vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">js</span>
|
||||
<pre
|
||||
class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line" v-if="props.scope !== 'instance' && props.from"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> { {{ props.memberof.split(".")[0] }} } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> "meteor/{{ props.from }}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">;</span></span>
|
||||
<span class="line" v-if="props.scope === 'instance'" v-html="comment(`// ${props.memberof} is an instance of ${getInstanceName(props.longname)}`)"></span>
|
||||
<span class="line" v-if="props.returns !== undefined" v-html="typeComment(getReturnType(props.returns))"></span>
|
||||
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"><span :style="{'display': props.returns === undefined ? 'none' : ''}"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">const </span>result = </span>{{ props.memberof }}.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">{{ trimFnName(props.fnName) }}</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(<span v-show="isOneLiner">);</span></span></span>
|
||||
<span class="line" v-for="(param, index) in props.params" :key="param.name"><span v-html="makePrimitiveHTML({ primitive: [param.type.names[0]], arr: props.params, index, isOptional: param.optional, name: param.name })"/></span>
|
||||
<span v-show="!isOneLiner" class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">);</span></span></code></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
19
v3-docs/docs/components/helpers/Locus.vue
Normal file
19
v3-docs/docs/components/helpers/Locus.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
<template>
|
||||
<div class="warning custom-block">
|
||||
<p>{{ props.locus }} only</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
locus: string;
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
div {
|
||||
margin: 0 !important;
|
||||
padding: 5px;
|
||||
}
|
||||
</style>
|
||||
132
v3-docs/docs/components/helpers/ParamTable.vue
Normal file
132
v3-docs/docs/components/helpers/ParamTable.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<script setup lang="ts">
|
||||
import { Collapse } from 'vue-collapsed'
|
||||
import { ref } from 'vue'
|
||||
import Caret from './Caret.vue'
|
||||
import { types } from 'util'
|
||||
|
||||
const copyArray = <T>(arr: T[]): T[] => {
|
||||
const newArr: T[] = []
|
||||
for (const item of arr) newArr.push(item)
|
||||
return newArr
|
||||
}
|
||||
const props = defineProps<{
|
||||
params: {
|
||||
name: string;
|
||||
type: { names: string[] };
|
||||
description: string;
|
||||
optional: boolean;
|
||||
}[];
|
||||
from: {
|
||||
lineNumber: number;
|
||||
filePath: string;
|
||||
}
|
||||
options?: { description: string, name: string, type: { names: string[] }; optional?: boolean }[]
|
||||
}>()
|
||||
const localArr = copyArray(props.params)
|
||||
const hasOptions = ({ params }: typeof props) => {
|
||||
for (const param of params) if (param.name === "options") return true
|
||||
}
|
||||
|
||||
const isOptionsTableOpen = ref(false);
|
||||
|
||||
function toggleOptionsTable() {
|
||||
isOptionsTableOpen.value = !isOptionsTableOpen.value
|
||||
}
|
||||
const showTypes = (types: string[]) => {
|
||||
const typesArr = copyArray(types)
|
||||
if (typesArr.length === 1) return typesArr[0]
|
||||
|
||||
const last = typesArr.pop()
|
||||
return typesArr.join(", ") + " or " + last
|
||||
}
|
||||
|
||||
const sourceCode = `https://github.com/meteor/meteor/blob/devel/packages/${props.from.filePath}#L${props.from.lineNumber}`
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="localArr.length > 0">
|
||||
<header>
|
||||
<h4>Arguments:</h4>
|
||||
<a :href="sourceCode">
|
||||
Source code
|
||||
</a>
|
||||
</header>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="param in localArr" :key="param.name">
|
||||
<td>{{ param.name }}</td>
|
||||
<td>{{ showTypes(param.type.names) }}</td>
|
||||
<template v-if="param.name === 'options'">
|
||||
<td>
|
||||
<span v-html="param.description"></span>
|
||||
<button v-if="(props.options?.length || -1) > 0" type="button" @click="toggleOptionsTable">
|
||||
{{ isOptionsTableOpen ? "Close" : "Open" }} options table
|
||||
<Caret :is-open="isOptionsTableOpen" />
|
||||
</button>
|
||||
</td>
|
||||
</template>
|
||||
<template v-else>
|
||||
<td v-html="param.description ?? `----`"></td>
|
||||
</template>
|
||||
<td>{{ param.optional ? "No" : "Yes" }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<Collapse v-if="hasOptions(props) && props.options && props.options?.length > 0" :when="isOptionsTableOpen"
|
||||
class="options-table">
|
||||
<h4>Options:</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="param in props.options" :key="param.name">
|
||||
<td>{{ param.name }}</td>
|
||||
<td>{{ param.type.names[0] }}</td>
|
||||
<td v-html="param.description ?? ``"></td>
|
||||
<td> No </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Collapse>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
table {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.options-table {
|
||||
--easing-dur: calc(var(--vc-auto-duration) * 1.5) cubic-bezier(0.33, 1, 0.68, 1);
|
||||
|
||||
transition:
|
||||
height var(--easing-dur),
|
||||
background-color var(--easing-dur),
|
||||
border-radius var(--easing-dur);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
109
v3-docs/docs/components/scripts/make-primitive-html.ts
Normal file
109
v3-docs/docs/components/scripts/make-primitive-html.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
const primitiveDefault = {
|
||||
function: () =>
|
||||
`<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> {}`,
|
||||
string: (name) =>
|
||||
`<span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;"> "${name}"`,
|
||||
number: () =>
|
||||
'<span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> 42',
|
||||
boolean: () =>
|
||||
'<span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> false',
|
||||
object: (name) =>
|
||||
`<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ${name}`,
|
||||
array: () => '<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> []',
|
||||
"array.<string>": () =>
|
||||
'<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">"string"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">]',
|
||||
"array.<object>": () =>
|
||||
'<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> [</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">{</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}]',
|
||||
"array.<ejsonable>": () =>
|
||||
`<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ${primitiveDefault.ejsonable(
|
||||
{
|
||||
pre: "[",
|
||||
post: "]",
|
||||
}
|
||||
)}`,
|
||||
// someProp: 'string', num: 42, bool: false, arr: [], obj: {}
|
||||
ejsonable(obj) {
|
||||
let pre, post;
|
||||
if (typeof obj === "string") pre = "";
|
||||
if (typeof obj === "object") {
|
||||
pre = obj.pre;
|
||||
post = obj.post;
|
||||
}
|
||||
if (pre.length > 1) pre = "";
|
||||
// if (post.length > 1) post = "";
|
||||
return `<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ${
|
||||
pre ?? ``
|
||||
}{ num:${primitiveDefault.number()}</span> , someProp:${primitiveDefault.string(
|
||||
"foo"
|
||||
)} </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}${
|
||||
post ?? ``
|
||||
}`;
|
||||
},
|
||||
ejson(pre, post) {
|
||||
return primitiveDefault.ejsonable(pre, post);
|
||||
},
|
||||
jsoncompatible(pre, post) {
|
||||
return primitiveDefault.ejsonable(pre, post);
|
||||
},
|
||||
promise: () =>
|
||||
'<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> Promise {</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">}',
|
||||
any: () => '<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> any',
|
||||
error: (name) =>
|
||||
`<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> Error(${name})`,
|
||||
mongoselector: () =>
|
||||
`<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> MongoSelector`,
|
||||
mongomodifier: () =>
|
||||
`<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> MongoModifier`,
|
||||
iterationcallback: () =>
|
||||
`<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> IterationCallback`,
|
||||
expresshandlerscallback: () =>
|
||||
`<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> ExpressHandlersCallback`,
|
||||
addruntimeconfighookcallback: () =>
|
||||
`<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> addRuntimeConfigHookCallback`,
|
||||
addupdatednotifyhookcallback: () =>
|
||||
`<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> addUpdatedNotifyHookCallback`,
|
||||
'tracker.computationfunction': () =>
|
||||
`<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> Tracker.ComputationFunction`,
|
||||
computation: () =>
|
||||
`<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> Computation`,
|
||||
func: () => primitiveDefault.function(),
|
||||
'tracker.computation': () => primitiveDefault.computation(),
|
||||
};
|
||||
|
||||
const comma = `<span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span>`;
|
||||
const br = `<br/>`;
|
||||
|
||||
const typeComment = (type) =>{
|
||||
return `<span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">/** </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">@returns</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> {${type}}</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"> */</span>`
|
||||
|
||||
}
|
||||
const comment = (text = " // this param is optional ") =>
|
||||
`<span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;">${text}</span>`;
|
||||
|
||||
const line = ({ html, pre, post } = { pre: "", post: "", html: "" }) =>
|
||||
`${pre}<span class="line">${html}</span>${post}`;
|
||||
|
||||
const makePrimitiveHTML = ({ primitive, arr, index, isOptional, name }) => {
|
||||
let n: string = primitive[0],
|
||||
primitiveName = n.toLowerCase(),
|
||||
value;
|
||||
|
||||
try {
|
||||
value = primitiveDefault[primitiveName](name);
|
||||
} catch (e) {
|
||||
console.error("primitive that we got:", primitive);
|
||||
throw new Error(
|
||||
`primitive ${primitiveName} is not registred in the map, here is the error: ${e}`
|
||||
);
|
||||
}
|
||||
|
||||
if (arr.length > 1)
|
||||
return line({
|
||||
html: value + `${comma}</span>`,
|
||||
pre: index === 0 ? "" : br,
|
||||
post: isOptional ? comment() : "",
|
||||
});
|
||||
return value + `</span>`;
|
||||
};
|
||||
|
||||
export { makePrimitiveHTML, comment, typeComment };
|
||||
145
v3-docs/docs/components/scripts/map-maker.js
Normal file
145
v3-docs/docs/components/scripts/map-maker.js
Normal file
@@ -0,0 +1,145 @@
|
||||
const makeUnique = (arr) => [...new Set(arr)];
|
||||
|
||||
const makeModules = (names) => {
|
||||
const modules = [];
|
||||
// check for modules (names with no dots or hashes)
|
||||
for (const name of names) {
|
||||
if (name.includes("#")) continue;
|
||||
if (name.includes(".")) continue;
|
||||
modules.push(name);
|
||||
}
|
||||
return modules;
|
||||
};
|
||||
|
||||
const makeApiList = (modules) => {
|
||||
let currentModule = modules[0];
|
||||
const apiList = {};
|
||||
for (const name of modules) {
|
||||
if (!apiList[currentModule]) {
|
||||
apiList[currentModule] = {
|
||||
[currentModule]: [],
|
||||
};
|
||||
}
|
||||
if (name.includes(currentModule)) {
|
||||
apiList[currentModule][name] = [];
|
||||
continue;
|
||||
}
|
||||
currentModule = name;
|
||||
}
|
||||
return apiList;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Array<string} names
|
||||
* @returns {Object<string, Object<string, Array<string>>}
|
||||
*/
|
||||
export function createMap(names) {
|
||||
/**
|
||||
* @type {string[]}
|
||||
*/
|
||||
const modules = makeModules(names);
|
||||
|
||||
/**
|
||||
* @type {Object<string, Object<string, Array<string>>}
|
||||
*/
|
||||
const apiList = makeApiList(modules);
|
||||
|
||||
const MODULES_TO_ADD = {
|
||||
module: { module: [] },
|
||||
Session: { Session: [] },
|
||||
Random: { Random: [] },
|
||||
Email: { Email: [] },
|
||||
DDPRateLimiter: { DDPRateLimiter: [] },
|
||||
};
|
||||
|
||||
Object.assign(apiList, MODULES_TO_ADD);
|
||||
|
||||
// populating map with links
|
||||
for (const api of Object.keys(apiList)) {
|
||||
const links = apiList[api];
|
||||
for (const link of Object.keys(links)) {
|
||||
const allApis = names.filter(
|
||||
(name) => name.includes(link + ".") || name.includes(link + "#")
|
||||
);
|
||||
apiList[api][link] = makeUnique(allApis);
|
||||
}
|
||||
}
|
||||
|
||||
// break App and WebApp
|
||||
|
||||
const webApp = apiList.App.App.filter((name) => name.includes("WebApp"));
|
||||
const app = apiList.App.App.filter((name) => !name.includes("WebApp"));
|
||||
|
||||
apiList.App.App = app;
|
||||
apiList.WebApp = {
|
||||
WebApp: webApp,
|
||||
};
|
||||
|
||||
// delete missplaced apis
|
||||
const TO_IGNORE = [
|
||||
"addRuntimeConfigHookCallback(options)",
|
||||
"addUpdatedNotifyHookCallback(options)",
|
||||
"currentUser",
|
||||
"expressHandlersCallback(req, res, next)",
|
||||
"getPublicationStrategy",
|
||||
"loggingIn",
|
||||
"main",
|
||||
"loggingOut",
|
||||
"IterationCallback",
|
||||
"CompileStep",
|
||||
"Cordova",
|
||||
"Plugin"
|
||||
];
|
||||
|
||||
|
||||
for (const key of Object.keys(apiList))
|
||||
if (TO_IGNORE.includes(key)) delete apiList[key];
|
||||
|
||||
// reroute DDP to -> meteor.html#DDP-connect
|
||||
apiList.DDP.shouldGoTo = '/api/meteor';
|
||||
// Match -> check#Match
|
||||
apiList.check.shouldGoTo = '/api/check';
|
||||
// Mongo -> collections.html#Mongo
|
||||
apiList.Mongo.shouldGoTo = '/api/collections';
|
||||
// Npm -> package.html#Npm
|
||||
apiList.Npm.shouldGoTo = '/api/package';
|
||||
// Subscription -> meteor.html#Meteor-publish
|
||||
apiList.Subscription.shouldGoTo = '/api/meteor';
|
||||
// Module -> packages/hot-module-replacement.html
|
||||
apiList.module.shouldGoTo = '/packages/hot-module-replacement';
|
||||
// Random -> packages/random.html
|
||||
apiList.Random.shouldGoTo = '/packages/random';
|
||||
// WebApp -> packages/webapp.html
|
||||
apiList.WebApp.shouldGoTo = '/packages/webapp';
|
||||
return apiList;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} filter
|
||||
* @returns {Object<string, Object<string, Array<string>>}
|
||||
* @returns {Object<string, Object<string, Array<string>>}
|
||||
*/
|
||||
export function filterMap(filter, apiList) {
|
||||
if (filter === "") {
|
||||
return apiList;
|
||||
}
|
||||
const newList = {};
|
||||
for (const api in apiList) {
|
||||
const newLinks = {};
|
||||
for (const key in apiList[api]) {
|
||||
const links = apiList[api][key];
|
||||
const newLinksArray = links.filter((link) => {
|
||||
return link.toLowerCase().includes(filter.toLowerCase());
|
||||
});
|
||||
if (newLinksArray.length > 0) {
|
||||
newLinks[key] = newLinksArray;
|
||||
}
|
||||
}
|
||||
if (Object.keys(newLinks).length > 0) {
|
||||
newList[api] = newLinks;
|
||||
}
|
||||
}
|
||||
return newList;
|
||||
}
|
||||
1
v3-docs/docs/data/.gitkeep
Normal file
1
v3-docs/docs/data/.gitkeep
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
36
v3-docs/docs/generators/changelog/EXAMPLE.MD
Normal file
36
v3-docs/docs/generators/changelog/EXAMPLE.MD
Normal file
@@ -0,0 +1,36 @@
|
||||
## vX.XX.X, 2023-XX-XX
|
||||
|
||||
### Highlights
|
||||
|
||||
* MongoDB Server 6.x Support
|
||||
* Embedded Mongo now uses MongoDB 6.0.3
|
||||
* Some pr [GH someone] [PR #number] // this will become -> [someone](https://github.com/someone) [PR](https://github.com/meteor/meteor/pull/number)
|
||||
#### Breaking Changes
|
||||
|
||||
N/A
|
||||
|
||||
#### Internal API changes
|
||||
|
||||
N/A
|
||||
|
||||
#### Migration Steps
|
||||
|
||||
TODO
|
||||
|
||||
#### Meteor Version Release
|
||||
|
||||
|
||||
* `Command line`:
|
||||
- Corrected typo in XX XX
|
||||
|
||||
* `npm mongo @4.13.0`: // You can use @get-version to get the version of the package
|
||||
- Updated MongoDB driver to version 4.13.0
|
||||
|
||||
#### Special thanks to
|
||||
|
||||
- [@XXX](https://github.com/XXXX).
|
||||
|
||||
|
||||
For making this great framework even better!
|
||||
|
||||
|
||||
48
v3-docs/docs/generators/changelog/README.md
Normal file
48
v3-docs/docs/generators/changelog/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
## Changelog Generator
|
||||
|
||||
This is a generator for the changelog, you must create a file with the name of
|
||||
the version that you are generating the changelog for. The script will take care of the rest.
|
||||
|
||||
In this file you should follow the EXAMPLE.md file that is within this directory.
|
||||
|
||||
The script will generate a file called `history.gen.md` that will be used by the
|
||||
`changelog.md` file to generate the changelog page.
|
||||
|
||||
To get which branches were merged into release you can search in the GitHub
|
||||
repo by using this query:
|
||||
|
||||
```
|
||||
is:pr base:<release-branch-name> is:merged
|
||||
```
|
||||
|
||||
or in GH Cli:
|
||||
|
||||
```bash
|
||||
gh pr list --state merged --base <release-branch-name>
|
||||
```
|
||||
|
||||
note that it may not be as useful as the first one, since it will not show the
|
||||
Authors and other related information.
|
||||
|
||||
## Why?
|
||||
|
||||
Computers with lower memory/ IDEs with high memory usage can have problems with
|
||||
the changelog file(~10k lines). This is a way to reduce the memory usage of the changelog, also creating a more
|
||||
organized changelog, since all the files will be reflecting at least one version.
|
||||
|
||||
## Update ordering.
|
||||
|
||||
If you want to make sure that the changelog is correcly ordered, take a look at the `order-packages.js` file.
|
||||
to use it, run the command below:
|
||||
|
||||
```bash
|
||||
node order-packages.js versions/3.0.md
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
node order-packages.js versions/<your-version>.md
|
||||
```
|
||||
|
||||
it will update the file with the correct ordering(this will override the file).
|
||||
81
v3-docs/docs/generators/changelog/order-packages.js
Normal file
81
v3-docs/docs/generators/changelog/order-packages.js
Normal file
@@ -0,0 +1,81 @@
|
||||
const fs = require("fs").promises;
|
||||
|
||||
// we want to get the strings that are between #### Breaking Changes and #### New Public API
|
||||
// then we will create a map with the package name and the version for example:
|
||||
//
|
||||
// - `accounts-2fa@3.0.0`:
|
||||
|
||||
// - Some methods are now async. See below:
|
||||
// - `Accounts._is2faEnabledForUser`
|
||||
// - `(Meteor Method) - generate2faActivationQrCode`
|
||||
// - `(Meteor Method) - enableUser2fa`
|
||||
// - `(Meteor Method) - disableUser2fa`
|
||||
// - `(Meteor Method) - has2faEnabled`
|
||||
|
||||
// will be converted to:
|
||||
// {"accounts-2fa@3.0.0": ` - Some methods are now async. See below:
|
||||
// - `Accounts._is2faEnabledForUser`
|
||||
// - `(Meteor Method) - generate2faActivationQrCode`
|
||||
// - `(Meteor Method) - enableUser2fa`
|
||||
// - `(Meteor Method) - disableUser2fa`
|
||||
// - `(Meteor Method) - has2faEnabled``
|
||||
// }
|
||||
|
||||
// then we will iterate and order the packages in alphabetical order and write again to the file.
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {Promise<[string, null] | ["", Error]>}
|
||||
*/
|
||||
async function getFile(path) {
|
||||
try {
|
||||
const data = await fs.readFile(path, "utf8");
|
||||
return [data, null];
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return ["", new Error("could not read file")];
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [filePath] = process.argv.slice(2);
|
||||
const [code, error] = await getFile(filePath);
|
||||
if (error) throw error;
|
||||
|
||||
const regex = /#### Breaking Changes([\s\S]*?)#### New Public API/gm;
|
||||
const matches = code.match(regex).join("\n").split("\n");
|
||||
|
||||
let objectMap = {};
|
||||
let currentWorkingPackage = "";
|
||||
for (const line of matches) {
|
||||
if (line.startsWith("-")) {
|
||||
const packageName = line
|
||||
.replace("-", "")
|
||||
.replace("`:", "")
|
||||
.replace("`", "")
|
||||
.trim();
|
||||
objectMap[packageName] = "";
|
||||
currentWorkingPackage = packageName;
|
||||
continue;
|
||||
}
|
||||
objectMap[currentWorkingPackage] += line + "\n";
|
||||
}
|
||||
// sorting acc
|
||||
const result = Object.keys(objectMap)
|
||||
.reduce((acc, key) => {
|
||||
if (key === "") return acc;
|
||||
acc.push({ key, value: objectMap[key]});
|
||||
return acc;
|
||||
}, [])
|
||||
.sort((a, b) => a.key.localeCompare(b.key))
|
||||
.reduce((acc, { key, value }) => {
|
||||
return acc + `- \`${key}\`:\n${value}`;
|
||||
}, "")
|
||||
|
||||
const newCode = code.replace(regex, `#### Breaking Changes\n\n${result}`);
|
||||
|
||||
await fs.writeFile(filePath, newCode);
|
||||
}
|
||||
|
||||
main().then(() => console.log("done"));
|
||||
110
v3-docs/docs/generators/changelog/script.js
Executable file
110
v3-docs/docs/generators/changelog/script.js
Executable file
@@ -0,0 +1,110 @@
|
||||
const _fs = require("fs");
|
||||
const fs = _fs.promises;
|
||||
function getPackageVersion(packageName) {
|
||||
function getFile(path) {
|
||||
try {
|
||||
const data = _fs.readFileSync(path, "utf8");
|
||||
return [data, null];
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return ["", e];
|
||||
}
|
||||
}
|
||||
|
||||
const [code, error] = getFile(`../../packages/${packageName}/package.js`);
|
||||
if (error) return "";
|
||||
for (const line of code.split(/\n/)) {
|
||||
// verify if the line has a version
|
||||
if (!line.includes("version:")) continue;
|
||||
|
||||
//Package.describe({
|
||||
// summary: 'some description.',
|
||||
// version: '1.2.3' <--- this is the line we want, we assure that it has a version in the previous if
|
||||
//});
|
||||
const [_, versionValue] = line.split(":");
|
||||
if (!versionValue) continue;
|
||||
const removeQuotes = (v) =>
|
||||
v.trim().replace(",", "").replace(/'/g, "").replace(/"/g, "");
|
||||
if (versionValue.includes("-"))
|
||||
return removeQuotes(versionValue.split("-")[0]);
|
||||
return removeQuotes(versionValue);
|
||||
}
|
||||
}
|
||||
|
||||
exports.generateChangelog = async () => {
|
||||
try {
|
||||
console.log("started concatenating files");
|
||||
const files = await fs.readdir("./generators/changelog/versions", "utf8");
|
||||
const filesStream = files
|
||||
.map((file) => {
|
||||
console.log(`reading file: ${file}`);
|
||||
return {
|
||||
fileName: file,
|
||||
buf: fs.readFile(`./generators/changelog/versions/${file}`, "utf8"),
|
||||
};
|
||||
})
|
||||
.map(async ({ buf, fileName }, index) => {
|
||||
// first file we don't do anything
|
||||
// Big file and does not follow the new standard
|
||||
if (index === 0) return buf;
|
||||
const content = (await buf).toString();
|
||||
/**
|
||||
* @type {Set<string>}
|
||||
*/
|
||||
const contribuitors = new Set();
|
||||
|
||||
// DSL Replacers
|
||||
// [PR #123] -> [PR #123](https://github.com/meteor/meteor/pull/123)
|
||||
// [GH meteor/meteor] -> [meteor/meteor](https://github.com/meteor/meteor)
|
||||
// package-name@get-version -> package-name@1.3.3
|
||||
|
||||
const file = content
|
||||
.replace(
|
||||
/\[PR #(\d+)\]/g,
|
||||
(_, number) =>
|
||||
`[PR](https://github.com/meteor/meteor/pull/${number})`
|
||||
)
|
||||
.replace(/\[GH ([^\]]+)\]/g, (_, name) => {
|
||||
contribuitors.add(name);
|
||||
return `[${name}](https://github.com/${name})`;
|
||||
})
|
||||
.replace(
|
||||
/([a-z0-9-]+)@get-version/g,
|
||||
(_, name) => `${name}@${getPackageVersion(name)}`
|
||||
);
|
||||
|
||||
// already have the contribuitors thanks in the file
|
||||
if (
|
||||
file.includes("#### Special thanks to") ||
|
||||
file.includes("[//]: # (Do not edit this file by hand.)")
|
||||
)
|
||||
return file;
|
||||
|
||||
// add the contribuitors
|
||||
const contribuitorsList = Array.from(contribuitors)
|
||||
.map((name) => `- [@${name}](https://github.com/${name}).`)
|
||||
.join("\n");
|
||||
|
||||
const doneFile = `${file}\n\n#### Special thanks to\n\n${contribuitorsList}\n\n`;
|
||||
|
||||
//SIDE EFFECTS
|
||||
// so that this is not ran every time, we will update the last file.
|
||||
// this is for the expensive part of the script
|
||||
if (index === files.length - 2)
|
||||
await fs.writeFile(
|
||||
`./generators/changelog/versions/${fileName}`,
|
||||
doneFile
|
||||
);
|
||||
return doneFile;
|
||||
})
|
||||
.reverse();
|
||||
console.log("Giving some touches to the files");
|
||||
const filesContent = await Promise.all(filesStream);
|
||||
await fs.writeFile("./history.md", filesContent.join(""));
|
||||
console.log("Finished :)");
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
1699
v3-docs/docs/generators/changelog/versions/0-before-2.10.md
Normal file
1699
v3-docs/docs/generators/changelog/versions/0-before-2.10.md
Normal file
File diff suppressed because it is too large
Load Diff
84
v3-docs/docs/generators/changelog/versions/2.10.md
Normal file
84
v3-docs/docs/generators/changelog/versions/2.10.md
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
## v2.10.0, 2023-01-13
|
||||
|
||||
### Highlights
|
||||
|
||||
* Update skeletons to use React 18 [PR](https://github.com/meteor/meteor/pull/12419) by [StorytellerCZ](https://github.com/StorytellerCZ).
|
||||
* Use MongoDB types instead of the homebuilt [PR](https://github.com/meteor/meteor/pull/12415) by [perbergland](https://github.com/perbergland).
|
||||
* Fixing wrong type definitions in MongoDB package [PR](https://github.com/meteor/meteor/pull/12409) by [ebroder](https://github.com/ebroder).
|
||||
* Typescript to version v4.7.4 [PR](https://github.com/meteor/meteor/pull/12393) by [StorytellerCZ](https://github.com/StorytellerCZ).
|
||||
* Update test-in-browser dependencies [PR](https://github.com/meteor/meteor/pull/12384) by [harryadel](https://github.com/harryadel).
|
||||
* Update boilerplate-generator-tests [PR](https://github.com/meteor/meteor/pull/12429) by [harryadel](https://github.com/harryadel).
|
||||
* Replace double-ended-queue with denque [PR](https://github.com/meteor/meteor/pull/12430) by [harryadel](https://github.com/harryadel).
|
||||
* Allow multiple runtime config and updated runtime hooks [PR](https://github.com/meteor/meteor/pull/12426) by [ebroder](https://github.com/ebroder).
|
||||
* Added async forEach and clear for method Hooks [PR](https://github.com/meteor/meteor/pull/12427) by [Grubba27](https://github.com/Grubba27).
|
||||
* Implemented async Tracker with explicit values [PR](https://github.com/meteor/meteor/pull/12294) by [radekmie](https://github.com/radekmie).
|
||||
* Improved eslint config [PR](https://github.com/meteor/meteor/pull/12309) by [afrokick](https://github.com/afrokick).
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
N/A
|
||||
|
||||
#### Internal API changes
|
||||
|
||||
N/A
|
||||
|
||||
#### Migration Steps
|
||||
|
||||
N/A
|
||||
|
||||
#### Meteor Version Release
|
||||
|
||||
* `babel-compiler@7.10.2`:
|
||||
- Updated @meteorjs/babel to version 7.18.0.
|
||||
- Updated to typescript to version v4.7.4.
|
||||
|
||||
* `boilerplate-generator-tests@1.5.1`:
|
||||
- Updated parse5 and turned streamToString into a local function.
|
||||
|
||||
* `callback-hook@1.5.0`
|
||||
- Added forEachAsync.
|
||||
|
||||
* `ecmascript@0.16.5`
|
||||
- Updated typescript to version 4.7.4.
|
||||
|
||||
* `Command line`:
|
||||
- Updated React skeletons to use React 18
|
||||
|
||||
* `Meteor@1.11.0`:
|
||||
- Replaced double-ended-queue with [denque](https://github.com/invertase/denque)
|
||||
|
||||
* `mongo@1.16.4`:
|
||||
- Fixed wrong type definitions.
|
||||
- switch to using MongoDB types instead of the homebuilt.
|
||||
- Fixed wrong type definitions in MongoDB package related to dropIndexAsync
|
||||
|
||||
* `react-fast-refresh@0.2.5`:
|
||||
- Updated react-refresh dependency.
|
||||
|
||||
* `test-in-browser@1.3.3`:
|
||||
- Updated dependencies and removed unused libs.
|
||||
|
||||
* `Tracker@1.3.0`:
|
||||
- Implemented async Tracker with explicit values
|
||||
|
||||
* `typescript@4.7.4`
|
||||
- Updated typescript to version 4.7.4.
|
||||
|
||||
* `webapp@1.13.3`
|
||||
- The forEach method on Hook will stop iterating unless the iterator function returns a truthy value.
|
||||
Previously, this meant that only the first registered runtime config hook would be called.
|
||||
|
||||
* `@meteorjs/babel@7.18.0-beta.5`
|
||||
- Updated typescript to version 4.7.4.
|
||||
|
||||
#### Special thanks to
|
||||
- [@StorytellerCZ](https://github.com/StorytellerCZ).
|
||||
- [@perbergland](https://github.com/perbergland).
|
||||
- [@ebroder](https://github.com/ebroder).
|
||||
- [@harryadel](https://github.com/harryadel).
|
||||
- [@radekmie](https://github.com/radekmie).
|
||||
- [@Grubba27](https://github.com/Grubba27).
|
||||
- [@afrokick](https://github.com/afrokick).
|
||||
|
||||
For making this great framework even better!
|
||||
137
v3-docs/docs/generators/changelog/versions/2.11.md
Normal file
137
v3-docs/docs/generators/changelog/versions/2.11.md
Normal file
@@ -0,0 +1,137 @@
|
||||
## v2.11.0, 2023-03-02
|
||||
|
||||
### Highlights
|
||||
|
||||
* MongoDB Server 6.x Support
|
||||
* Embedded Mongo now uses MongoDB 6.0.3
|
||||
* Optimized makeLookupFunction
|
||||
by [radekmie](https://github.com/radekmie) [PR](https://github.com/meteor/meteor/pull/12462)
|
||||
* In async wrappers, catch exceptions and reject
|
||||
by [ebroder](https://github.com/ebroder) [PR](https://github.com/meteor/meteor/pull/12469)
|
||||
* Bump Typescript to v4.9.4 by [Firfi](https://github.com/Firfi) [PR](https://github.com/meteor/meteor/pull/12465)
|
||||
* Ensure the meteor.loginServiceConfiguration subscription always becomes ready
|
||||
by [Torgen](https://github.com/Torgen) [PR](https://github.com/meteor/meteor/pull/12480)
|
||||
* Deprecate appcache package
|
||||
by [StorytellerCZ](https://github.com/StorytellerCZ) [PR](https://github.com/meteor/meteor/pull/12456)
|
||||
* Made standard-minifier-css debuggable
|
||||
by [softwarecreations](https://github.com/softwarecreations) [PR](https://github.com/meteor/meteor/pull/12478).
|
||||
* Upgrading MongoDB Driver to v4.14
|
||||
by [Grubba27](https://github.com/Grubba27) [PR](https://github.com/meteor/meteor/pull/12501)
|
||||
* Remove Blaze dependency and types that live in blaze.d.ts
|
||||
by [perbergland](https://github.com/perbergland) [PR](https://github.com/meteor/meteor/pull/12428)
|
||||
|
||||
* Switch typescript skeleton to zodern:types and test that it works by [GH ebroder] [PR #12510]
|
||||
* Remove packages/*/.npm from gitignore and add missing .npm folders by [GH ebroder] [PR #12508]
|
||||
* Add type definitions for async methods from Meteor 2.9 by [GH ebroder] [PR #12507]
|
||||
* TypeScript skeleton fixes by [GH ebroder] [PR #12506]
|
||||
* Fix TypeScript type dependencies for mongo, webapp, and underscore by [GH ebroder] [PR #12505]
|
||||
* Improve specificity of types previously declared as "Object" by [GH ebroder] [PR #12520]
|
||||
* Bump to Node 14.21.3 by [GH StorytellerCZ] [PR #12517]
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
`meteor mongo` command was removed due compatibility with MongoDB v6.x of `mongosh
|
||||
for more information about MongoDB migration
|
||||
read our [Migration Guide](https://guide.meteor.com/2.11-migration.html) for this version.
|
||||
|
||||
#### Internal API changes
|
||||
|
||||
App cache is now deprecated.
|
||||
|
||||
#### Migration Steps
|
||||
|
||||
Read our [Migration Guide](https://guide.meteor.com/2.11-migration.html) for this version.
|
||||
|
||||
#### Meteor Version Release
|
||||
|
||||
* `accounts-2fa@2.0.2`:
|
||||
- removed .npm/package contents and added .gitignore
|
||||
|
||||
* `accounts-base@2.2.7`:
|
||||
- Updated types to match async methods added in newer versions.
|
||||
- Ensured the meteor.loginServiceConfiguration subscription always becomes ready, by adding a this.ready() call.
|
||||
- Specified that previously were declared as "Object" types. More context can be seen in [PR #12520].
|
||||
|
||||
* `accounts-password@2.3.4`:
|
||||
- Updated `Accounts.changePassword` and `Accounts.resetPassword` to be correctly verify if the new password is
|
||||
valid.
|
||||
- removed .npm/package contents and added .gitignore
|
||||
|
||||
* `appcache@1.2.8`
|
||||
- Deprecated appcache
|
||||
package. [applicationCache](https://developer.mozilla.org/en-US/docs/Web/API/Window/applicationCache), which this
|
||||
package relies on, has been deprecated and is not available on the latest browsers.
|
||||
|
||||
* `babel-compiler@7.10.3`:
|
||||
- Updated typescript to version 4.9.4.
|
||||
|
||||
* `ecmascript@0.16.6`:
|
||||
- Updated typescript to version 4.9.4.
|
||||
|
||||
* `email@2.2.4`:
|
||||
- Updated types to match async methods added in newer versions.
|
||||
- Specified that previously were declared as "Object" types. More context can be seen in [PR #12520].
|
||||
|
||||
* `logging@1.3.2`:
|
||||
- removed .npm/package contents and added .gitignore
|
||||
|
||||
* `Command line`:
|
||||
- Corrected typo in vue skeleton.
|
||||
- Command `meteor mongo` was removed due compatibility with MongoDB v6.x of `mongosh`
|
||||
|
||||
* `meteor@1.11.1`:
|
||||
- updated types to removed unused Blaze types
|
||||
- Specified that previously were declared as "Object" types. More context can be seen in [PR #12520].
|
||||
|
||||
* `minimongo@1.9.2`:
|
||||
- Updated performance of makeLookupFunction
|
||||
- In async wrappers, catch exceptions and reject
|
||||
|
||||
* `mongo@1.16.5`:
|
||||
- In async wrappers, catch exceptions and reject
|
||||
- Updated MongoDB types to match driver version 4.13.0 and MongoDB server version 6.0.3
|
||||
- Specified that previously were declared as "Object" types. More context can be seen in [PR #12520].
|
||||
- Now uses MongoDB v6.0.3
|
||||
- Now uses Node v14.21.3
|
||||
|
||||
* `npm-mongo@4.14.0`:
|
||||
- Updated MongoDB driver to version 4.14.0
|
||||
|
||||
* `oauth@2.2.0`:
|
||||
- bumped cordova-plugin-inappbrowser to 5.0.0
|
||||
|
||||
* `react-fast-refresh@0.2.6`:
|
||||
- removed .npm/package contents and added .gitignore
|
||||
|
||||
* `standard-minifier-css@1.9.0`:
|
||||
- standard-minifier-css is now debuggable
|
||||
|
||||
* `tracker@1.3.1`:
|
||||
- Added missing withComputation method in types
|
||||
|
||||
* `typescript@4.9.4`
|
||||
- Updated typescript to version 4.9.4.
|
||||
|
||||
* `underscore@1.0.12`:
|
||||
- Added dependency in types to underscore
|
||||
|
||||
* `webapp@1.13.4`:
|
||||
- Added dependency in types to webapp(to connect)
|
||||
- removed .npm/package contents and added .gitignore
|
||||
|
||||
* `@meteorjs/babel@7.18.0-beta.6`
|
||||
- Updated typescript to version 4.9.4.
|
||||
|
||||
#### Special thanks to
|
||||
|
||||
- [@radekmie](https://github.com/radekmie).
|
||||
- [@ebroder](https://github.com/ebroder).
|
||||
- [@Firfi](https://github.com/Firfi).
|
||||
- [@Torgen](https://github.com/Torgen).
|
||||
- [@StorytellerCZ](https://github.com/StorytellerCZ).
|
||||
- [@softwarecreations](https://github.com/softwarecreations).
|
||||
- [@Grubba27](https://github.com/Grubba27).
|
||||
|
||||
For making this great framework even better!
|
||||
|
||||
|
||||
150
v3-docs/docs/generators/changelog/versions/2.12.md
Normal file
150
v3-docs/docs/generators/changelog/versions/2.12.md
Normal file
@@ -0,0 +1,150 @@
|
||||
## v2.12.0, 2023-04-28
|
||||
|
||||
### Highlights
|
||||
|
||||
* Document main function in webapp by [harryadel](https://github.com/harryadel) [PR](https://github.com/meteor/meteor/pull/12579)
|
||||
* Add undocument properties to docs by [harryadel](https://github.com/harryadel) [PR](https://github.com/meteor/meteor/pull/12563)
|
||||
* Bump NPM versions for css minifiers by [wreiske](https://github.com/wreiske) [PR](https://github.com/meteor/meteor/pull/12562)
|
||||
* Updated Email and Mongo package types by [ebroder](https://github.com/ebroder) [PR](https://github.com/meteor/meteor/pull/12554)
|
||||
* Updated security.md by [jamauro](https://github.com/jamauro) [PR](https://github.com/meteor/meteor/pull/12461)
|
||||
* Added addHtmlAttributeHook type on WebApp by [DblK](https://github.com/DblK) [PR](https://github.com/meteor/meteor/pull/12545)
|
||||
* Added loginServiceConfiguration type on Accounts by [DblK](https://github.com/DblK) [PR](https://github.com/meteor/meteor/pull/12539)
|
||||
* Add TS types for Mongo Collection countDocuments and estimatedDocumentCount by [ArthurHoaro](https://github.com/ArthurHoaro) [PR](https://github.com/meteor/meteor/pull/12533)
|
||||
* Allow setting a custom ddp-rate-limit message per rule by [wreiske](https://github.com/wreiske) [PR](https://github.com/meteor/meteor/pull/12082)
|
||||
* Updated MongoDB driver to 4.15 by [Grubba27](https://github.com/Grubba27) [PR](https://github.com/meteor/meteor/pull/12583)
|
||||
* Adding warn with env variable when using old apis vy [Grubba27](https://github.com/Grubba27) [PR](https://github.com/meteor/meteor/pull/12585)
|
||||
* Fix syntax for legacy client by [zodern](https://github.com/zodern) [PR](https://github.com/meteor/meteor/pull/12596)
|
||||
* Updating MongoDB driver to 4.16 by [Grubba27](https://github.com/Grubba27) [PR](https://github.com/meteor/meteor/pull/12599)
|
||||
* Update sockjs-client by [harryadel](https://github.com/harryadel) [PR](https://github.com/meteor/meteor/pull/12590)
|
||||
* [Accounts] set custom collection by [dmromanov](https://github.com/dmromanov) [PR](https://github.com/meteor/meteor/pull/12591)
|
||||
* Wrappers to help in the async migration by [matheusccastroo](https://github.com/matheusccastroo) [PR](https://github.com/meteor/meteor/pull/12593)
|
||||
* Mongo query hangs all clients subscribed to a query/collection by [KoenLav](https://github.com/KoenLav) [PR](https://github.com/meteor/meteor/pull/12587)
|
||||
* Blaze to 2.6.2 by [StorytellerCZ](https://github.com/StorytellerCZ) [PR](https://github.com/meteor/blaze/pull/411)
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
N/A
|
||||
|
||||
#### Internal API changes
|
||||
|
||||
N/A
|
||||
|
||||
#### Migration Steps
|
||||
|
||||
Now if you want to check where do you call old-style api methods
|
||||
you can use ```WARN_WHEN_USING_OLD_API``` before starting your meteor process.
|
||||
|
||||
|
||||
#### Meteor Version Release
|
||||
|
||||
* `accounts-base@2.2.8`:
|
||||
- Added `loginServiceConfiguration` type.
|
||||
- Added the `collection` option property, in order to be able to set the collection for Accounts,
|
||||
more can be seen in the [discussion](https://github.com/meteor/meteor/discussions/12544#discussioncomment-5240763)
|
||||
and in the [related issue](https://github.com/meteor/meteor-feature-requests/issues/20).
|
||||
- `onCreateUserHook` now accept promises and wait if necessary.
|
||||
|
||||
* `babel-compiler@7.10.4`:
|
||||
- Added `es5` compatible syntax.
|
||||
|
||||
* `browser-policy-content@1.1.2`:
|
||||
- Added `es5` compatible syntax.
|
||||
|
||||
* `browser-policy-framing@1.1.2`:
|
||||
- Added `es5` compatible syntax.
|
||||
|
||||
* `browser-policy@1.1.2`:
|
||||
- Updated test name.
|
||||
|
||||
* `callback-hook@1.5.1`:
|
||||
- Added async hooks possibility to make async migrations easier.
|
||||
|
||||
* `context@0.5.1`:
|
||||
- Added `es5` compatible syntax.
|
||||
|
||||
* `ddp-rate-limiter@1.2.0`:
|
||||
- Allow setting a custom ddp-rate-limit message per rule.
|
||||
|
||||
* `ddp-server@2.6.1`:
|
||||
- Updated sockjs version.
|
||||
|
||||
* `dev-error-overlay@0.1.2`:
|
||||
- Added `es5` compatible syntax by adding the `ecmascript` package.
|
||||
|
||||
* `dynamic-import@0.7.3`:
|
||||
- Added `es5` compatible syntax.
|
||||
|
||||
* `ecmascript@0.16.7`:
|
||||
- Updated tests location.
|
||||
|
||||
* `ecmascript-runtime@0.8.1`:
|
||||
- Updated npm dependencies.
|
||||
|
||||
* `email@2.2.5`:
|
||||
- Updated type `CustomEmailOptions` to be a type instead of an interface.
|
||||
|
||||
* `hot-module-replacement@0.5.3`:
|
||||
- Added `es5` compatible syntax.
|
||||
|
||||
|
||||
* `meteor@1.11.2`:
|
||||
- Added documentation for `isTest`, `isAppTest` and `isPackageTest` methods.
|
||||
- Added possibility to add async hooks to make async migrations easier. [PR](https://github.com/meteor/meteor/pull/12593)
|
||||
|
||||
* `minifier-css@1.6.4`:
|
||||
- Bump NPM versions for css minifiers.
|
||||
|
||||
* `minimongo@1.9.3`:
|
||||
- Updated to be able to track old api usage.
|
||||
|
||||
* `modules-runtime-hot@0.14.2`:
|
||||
- Added `es5` compatible syntax.
|
||||
|
||||
* `mongo@1.16.6`:
|
||||
- Added `countDocuments` and `estimatedDocumentCount` types.
|
||||
- Added warning for when old style apis are being used, to use this feature,
|
||||
use the variable`WARN_WHEN_USING_OLD_API=true` before starting the Meteor process.
|
||||
- Oplog driver updated to not throw error when MongoDB server and Meteor client mismatch. [issue](https://github.com/meteor/meteor/issues/12516)
|
||||
|
||||
* `non-core`:
|
||||
- Blaze to version 2.6.2.
|
||||
|
||||
* `npm-mongo@4.16.0`:
|
||||
- Updated MongoDB driver to 4.15.
|
||||
- Updated MongoDB driver to 4.16.
|
||||
|
||||
* `rate-limit@1.1.1`:
|
||||
- Added `ruleId` property that will be used for setting messages.
|
||||
|
||||
* `react-fast-refresh@0.2.7`:
|
||||
- Added `es5` compatible syntax.
|
||||
|
||||
* `socket-stream-client@0.5.0`:
|
||||
- Updated sockjs version.
|
||||
|
||||
* `standard-minifier-css@1.9.2`:
|
||||
- Bump NPM versions for css minifiers.
|
||||
|
||||
* `tracker@1.3.2`:
|
||||
- Updated types and updated JSDocs for `Tracker.withComputation`.
|
||||
|
||||
* `underscore@1.0.13`:
|
||||
- Updated npm dependencies.
|
||||
|
||||
* `webapp@1.13.5`:
|
||||
- Added `addHtmlAttributeHook` type.
|
||||
|
||||
|
||||
|
||||
#### Special thanks to
|
||||
|
||||
- [@harryadel](https://github.com/harryadel).
|
||||
- [@wreiske](https://github.com/wreiske).
|
||||
- [@ebroder](https://github.com/ebroder).
|
||||
- [@jamauro](https://github.com/jamauro).
|
||||
- [@DblK](https://github.com/DblK).
|
||||
- [@ArthurHoaro](https://github.com/ArthurHoaro).
|
||||
- [@Grubba27](https://github.com/Grubba27).
|
||||
- [@zodern](https://github.com/zodern).
|
||||
- [@dmromanov](https://github.com/dmromanov).
|
||||
- [@matheusccastroo](https://github.com/matheusccastroo).
|
||||
75
v3-docs/docs/generators/changelog/versions/2.13.0.md
Normal file
75
v3-docs/docs/generators/changelog/versions/2.13.0.md
Normal file
@@ -0,0 +1,75 @@
|
||||
## v2.13.0, 2023-07-26
|
||||
|
||||
### Highlights
|
||||
|
||||
* Handled implicit collection creation oplog message by [radekmie](https://github.com/radekmie) [PR](https://github.com/meteor/meteor/pull/12643).
|
||||
* Fix upsert logs when using WARN_WHEN_USING_OLD_API flag by [Grubba27](https://github.com/Grubba27) [PR](https://github.com/meteor/meteor/pull/12640).
|
||||
* Updating mongo types by [Grubba27](https://github.com/Grubba27) [PR](https://github.com/meteor/meteor/pull/12639).
|
||||
* Fix solid skeleton by [fredmaiaarantes](https://github.com/fredmaiaarantes) [PR](https://github.com/meteor/meteor/pull/12637).
|
||||
* Setting The Viewport meta tag on skeletons [fredmaiaarantes](https://github.com/fredmaiaarantes) [PR](https://github.com/meteor/meteor/pull/12636).
|
||||
* Update mongo.d.ts with projection [StorytellerCZ](https://github.com/StorytellerCZ) [PR](https://github.com/meteor/meteor/pull/12635).
|
||||
* Update guide code for GraphQL [StorytellerCZ](https://github.com/StorytellerCZ) [PR](https://github.com/meteor/meteor/pull/12619).
|
||||
* Twitter Whitelist issue resolved [Atharshoyeb](https://github.com/Atharshoyeb) [PR](https://github.com/meteor/meteor/pull/12369).
|
||||
* Node security patch (14.21.4) [PR](https://github.com/meteor/node-v14-esm/pull/1). Thanks a lot [denihs](https://github.com/denihs) for your contribuiton.
|
||||
* Updated deprecated reference in mongo package by [StorytellerCZ](https://github.com/StorytellerCZ) [PR](https://github.com/meteor/meteor/pull/12653/files).
|
||||
* Updated BlazeJS git ref in core meteor to 2.7.1 by [Grubba27](https://github.com/Grubba27) [PR](https://github.com/meteor/meteor/pull/12651).
|
||||
* Added `Meteor.applyAsync` types by [Julusian](https://github.com/Julusian) [PR](https://github.com/meteor/meteor/pull/12645).
|
||||
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
If you are running Meteor with docker you will
|
||||
need to update your docker file to use our [new docker image](https://hub.docker.com/r/meteor/node)
|
||||
that contains Nodejs v14.21.4.
|
||||
|
||||
#### Known issues
|
||||
|
||||
Please, [check our known issues page](https://docs.meteor.com/known-issues)
|
||||
for more information about the problems and issues you might find while migrating.
|
||||
|
||||
#### Internal changes
|
||||
|
||||
* `ddp-server@get-version`:
|
||||
- Updated livedata server test to be more easily debbuged.
|
||||
|
||||
* `mongo@get-version`:
|
||||
- Updated deprecated reference in Mongo package.
|
||||
|
||||
#### Migration Steps
|
||||
|
||||
|
||||
Please, follow our [migration guide](https://guide.meteor.com/2.13-migration) to understand what's needed to upgrade to Meteor 2.13.
|
||||
|
||||
#### Meteor Version Release
|
||||
|
||||
|
||||
* `Command line`:
|
||||
- Updated metatags for skeletons.
|
||||
- Updated solidjs skeleton to be more idiomatic.
|
||||
|
||||
* `meteor@1.11.3`:
|
||||
- Added types for applyAsync and added more documentation for applyAsync options.
|
||||
|
||||
* `mongo@1.16.7`:
|
||||
- Updated types with projection.
|
||||
- Fixed wrong upsert logs when using WARN_WHEN_USING_OLD_API flag.
|
||||
- Handled implicit collection creation oplog message.
|
||||
|
||||
* `test-in-console@1.2.5`:
|
||||
- Adjusted log indentation.
|
||||
- All errors will be logged to console.
|
||||
- Will always use puppeteer@20.4.0
|
||||
|
||||
* `twitter-oauth@1.3.3`:
|
||||
- Fixed twitter whitelist issue.
|
||||
|
||||
|
||||
#### Special thanks to
|
||||
|
||||
- [@radekmie](https://github.com/radekmie).
|
||||
- [@Grubba27](https://github.com/Grubba27).
|
||||
- [@fredmaiaarantes](https://github.com/fredmaiaarantes).
|
||||
- [@StorytellerCZ](https://github.com/StorytellerCZ).
|
||||
- [@Atharshoyeb](https://github.com/Atharshoyeb).
|
||||
- [@Julusian](https://github.com/Julusian).
|
||||
- [@denihs](https://github.com/denihs).
|
||||
40
v3-docs/docs/generators/changelog/versions/2.13.1.md
Normal file
40
v3-docs/docs/generators/changelog/versions/2.13.1.md
Normal file
@@ -0,0 +1,40 @@
|
||||
## v2.13.1, 2023-09-04
|
||||
|
||||
### Highlights
|
||||
|
||||
* Solved zlib issue with Meteor.js and ESM Node.js 14.21.4 [PR #12765] by (GH Grubba27).
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
N/A
|
||||
|
||||
#### Internal API changes
|
||||
|
||||
N/A
|
||||
|
||||
#### Migration Steps
|
||||
|
||||
Please run the following command to update your project:
|
||||
|
||||
```bash
|
||||
|
||||
meteor update --release 2.13.1
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### Meteor Version Release
|
||||
|
||||
|
||||
* `Command line`:
|
||||
- The bundle version was changed to 14.21.4.1 to use another compiled version of the [ESM Node.js](https://guide.meteor.com/using-node-v14.21.4).
|
||||
|
||||
|
||||
#### Special thanks to
|
||||
|
||||
- [@Grubba27](https://github.com/Grubba27).
|
||||
|
||||
|
||||
For making this great framework even better!
|
||||
|
||||
|
||||
41
v3-docs/docs/generators/changelog/versions/2.13.3.md
Normal file
41
v3-docs/docs/generators/changelog/versions/2.13.3.md
Normal file
@@ -0,0 +1,41 @@
|
||||
## v2.13.3, 2023-09-08
|
||||
|
||||
### Highlights
|
||||
|
||||
* Solves the issue [#12771: Version 2.13.1 suddenly requires a newer glibc version](https://github.com/meteor/meteor/issues/12771).
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
N/A
|
||||
|
||||
#### Internal API changes
|
||||
|
||||
N/A
|
||||
|
||||
#### Migration Steps
|
||||
|
||||
Please run the following command to update your project:
|
||||
|
||||
```bash
|
||||
|
||||
meteor update --release 2.13.3
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### Meteor Version Release
|
||||
|
||||
|
||||
* `Command line`:
|
||||
- The bundle version was changed to 14.21.4.3 to use another compiled version of the [ESM Node.js](https://guide.meteor.com/using-node-v14.21.4). The previous version was generated using a different unix distribution (Ubuntu) while we should use CentOS.
|
||||
|
||||
|
||||
#### Special thanks to
|
||||
|
||||
- [@aquinoit](https://github.com/aquinoit).
|
||||
- [@fredmaiaarantes](https://github.com/fredmaiaarantes).
|
||||
- [@Grubba27](https://github.com/Grubba27).
|
||||
|
||||
For making this great framework even better!
|
||||
|
||||
|
||||
711
v3-docs/docs/generators/changelog/versions/3.0.md
Normal file
711
v3-docs/docs/generators/changelog/versions/3.0.md
Normal file
@@ -0,0 +1,711 @@
|
||||
## v3.0, TBD
|
||||
|
||||
### Highlights
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
- `accounts-2fa@3.0.0`:
|
||||
|
||||
- Some methods are now async. See below:
|
||||
- `Accounts._is2faEnabledForUser`
|
||||
- `(Meteor Method) - generate2faActivationQrCode`
|
||||
- `(Meteor Method) - enableUser2fa`
|
||||
- `(Meteor Method) - disableUser2fa`
|
||||
- `(Meteor Method) - has2faEnabled`
|
||||
|
||||
- `accounts-base@3.0.0`:
|
||||
|
||||
- `methods.removeOtherTokens` is now async
|
||||
- `Accounts.destroyToken` is now async
|
||||
- `Accounts.insertUserDoc` is now async
|
||||
- `Accounts.updateOrCreateUserFromExternalService` is now async
|
||||
- `Accounts.expirePasswordToken` is now async
|
||||
- `Accounts.setupUsersCollection` is now async
|
||||
- `Meteor.user` is now async in server
|
||||
|
||||
- `accounts-facebook@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `accounts-github@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `accounts-google@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `accounts-meetup@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `accounts-meteor-developer@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `accounts-oauth@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `accounts-password@3.0.0`:
|
||||
|
||||
- Some server methods are now async:
|
||||
- `Accounts.sendResetPasswordEmail`
|
||||
- `Accounts.sendEnrollmentEmail`
|
||||
- `Accounts.sendVerificationEmail`
|
||||
- `Accounts.addEmail`
|
||||
- `Accounts.removeEmail`
|
||||
- `Accounts.verifyEmail`
|
||||
- `Accounts.createUserVerifyingEmail`
|
||||
- `Accounts.createUser`
|
||||
- `Accounts.generateVerificationToken`
|
||||
- `Accounts.generateResetToken`
|
||||
- `Accounts.forgotPassword`
|
||||
- `Accounts.setPassword`
|
||||
- `Accounts.changePassword`
|
||||
- `Accounts.setUsername`
|
||||
- `Accounts.findUserByEmail`
|
||||
- `Accounts.findUserByUsername`
|
||||
|
||||
- `accounts-passwordless@3.0.0`:
|
||||
|
||||
- `Accounts.sendLoginTokenEmail` is now async.
|
||||
|
||||
- `accounts-twitter@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `accounts-ui-unstyled@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `accounts-ui@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `accounts-weibo@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `allow-deny@2.0.0`:
|
||||
|
||||
- Updated to accept async functions.
|
||||
|
||||
- `appcache@2.0.0`:
|
||||
|
||||
- Updated internal api to use `handlers`
|
||||
|
||||
- `audit-argument-checks@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `autopublish@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `autoupdate@2.0.0`:
|
||||
|
||||
- Updated api to be async, with asyncronous queueing.
|
||||
|
||||
- `babel-compiler@8.0.0`:
|
||||
|
||||
- Removed `Promise.await` default transform.
|
||||
- Added top-level-await to packages.
|
||||
|
||||
- `babel-runtime@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `base64@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `binary-heap@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `blaze@3.0.0`:
|
||||
- Todo
|
||||
|
||||
- `boilerplate-generator-tests@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `boilerplate-generator@2.0.0`:
|
||||
|
||||
- `toHTML` is no longer available (it was already deprecated). Use `toHTMLStream` instead.
|
||||
- Updated to use `handlers`
|
||||
|
||||
- `browser-policy-common@2.0.0`:
|
||||
|
||||
- Updated to use `handlers`
|
||||
|
||||
- `browser-policy-content@2.0.0`:
|
||||
|
||||
- Some methods are now async. See below:
|
||||
- `BrowserPolicy.content.setPolicy`
|
||||
- `BrowserPolicy.content.allowInlineScripts`
|
||||
- `BrowserPolicy.content.disallowInlineScripts`
|
||||
- `BrowserPolicy.content.disallowAll`
|
||||
- `BrowserPolicy.setDefaultPolicy`
|
||||
|
||||
- `browser-policy-framing@2.0.0`:
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `browser-policy@2.0.0`:
|
||||
Updated to use async methods from `browser-policy-common` and `browser-policy-content`.
|
||||
|
||||
- `caching-compiler@2.0.0`:
|
||||
|
||||
- `afterLink` is now async.
|
||||
- Updated to use now async API.
|
||||
|
||||
- `callback-hook@2.0.0`:
|
||||
|
||||
- Added `forEachAsync` method.
|
||||
|
||||
- `check@2.0.0`:
|
||||
|
||||
- Removed `fibers` related tests.
|
||||
|
||||
- `constraint-solver@2.0.0`:
|
||||
|
||||
- Some methods are now async. See below:
|
||||
|
||||
- `ConstraintSolver.getVersionCostSteps`
|
||||
- `ConstraintSolver.analyze`
|
||||
- `ConstraintSolver.resolve`
|
||||
|
||||
- Updated tests to be async.
|
||||
- Removed a few underscore usage.
|
||||
- Added updated to use async methods
|
||||
|
||||
- `context@1.0.0`:
|
||||
|
||||
- Removed `fibers` from package.
|
||||
|
||||
- `core-runtime@2.0.0`:
|
||||
|
||||
- Created package to load packages and the app.
|
||||
- This is the pakcages that sets up the Runtime.
|
||||
|
||||
- `crosswalk@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `ddp-client@3.0.0`:
|
||||
|
||||
- Added `isAsyncCall` method to know if call is being made by a async method.
|
||||
- Removed `fibers` from package.
|
||||
- Updated tests to use async methods.
|
||||
|
||||
- `ddp-common@2.0.0`:
|
||||
|
||||
- Added `.fence` option.
|
||||
|
||||
- `ddp-rate-limiter@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `ddp-server@3.0.0`:
|
||||
|
||||
- Updated to use async methods.
|
||||
- Removed `fibers` from package.
|
||||
- Updated tests to use async methods.
|
||||
- Turned server implementation to async.
|
||||
|
||||
- `ddp@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `diff-sequence@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `disable-oplog@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `ecmascript-runtime-client@1.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `ecmascript-runtime-server@1.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `ecmascript-runtime@1.0.0`:
|
||||
|
||||
- Added dependency to `@babel/runtime`.
|
||||
|
||||
- `ecmascript@1.0.0`:
|
||||
|
||||
- Added dependency to `@babel/runtime`.
|
||||
- Moved runtime tests.
|
||||
|
||||
- `ejson@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `email@3.0.0`:
|
||||
|
||||
- `Email.send` is no longer available. Use `Email.sendAsync` instead.
|
||||
- Updated types to reflext async methods and `Email.send` depracation.
|
||||
|
||||
- `es5-shim@5.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `facebook-config-ui@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `facebook-oauth@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `facts-base@2.0.0`:
|
||||
|
||||
- turned unorderd deps on `ddp` to false.
|
||||
|
||||
- `facts-ui@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `fetch@1.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `force-ssl-common@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `force-ssl@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `geojson-utils@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `github-config-ui@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `github-oauth@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `google-config-ui@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `google-oauth@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `hot-code-push@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `http@`:
|
||||
- Updated handlers to use `handlers`
|
||||
- `id-map@2.0.0`:
|
||||
|
||||
- Added `forEachAsync` method.
|
||||
|
||||
- `insecure@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `inter-process-messaging@1.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `launch-screen@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `localstorage@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `logging@2.0.0`:
|
||||
|
||||
- Added dependency to `@babel/runtime`.
|
||||
|
||||
- `logic-solver@3.0.0`:
|
||||
`Logic.disablingAssertions` is now async.
|
||||
`minMaxWS` is now async.
|
||||
|
||||
- `meetup-config-ui@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `meetup-oauth@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `meteor-base@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `meteor-developer-config-ui@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `meteor-developer-oauth@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `meteor-tool@3.0.0`:
|
||||
|
||||
- Changes to how meteor apps are being created [PR](https://github.com/meteor/meteor/pull/12697)
|
||||
|
||||
- `meteor@2.0.0`:
|
||||
|
||||
- Async local storage was added to help deal with async methods.
|
||||
- Added `promiseEmmiter` to help with async methods.
|
||||
- Removed `fibers` from package.
|
||||
- `Meteor.absoluteUrl` in localhost uses `127.0.1` by default.
|
||||
|
||||
- `minifier-css@2.0.0`:
|
||||
|
||||
- `minifyCss` is now async.
|
||||
- Removed `fibers` from package.
|
||||
|
||||
- `minifier-js@3.0.0`:
|
||||
|
||||
- `minifyJs` is now async.
|
||||
- `terserMinify` no longer takes callbacks
|
||||
- Removed `fibers` from package.
|
||||
|
||||
* `minimongo@2.0.0`:
|
||||
- `cursor.observe` now returns `isReady` and `isReadyPromise` wich indicates
|
||||
if the cursor is ready and if the callbacks are have been called.
|
||||
If you only use it in the `Client` or as a `LocalCollection` things have not
|
||||
changed.
|
||||
|
||||
- `mobile-experience@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `mobile-status-bar@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `modern-browsers@1.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `modules-runtime@1.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `modules@1.0.0`:
|
||||
|
||||
- Updated `reify` version.
|
||||
|
||||
- `mongo-decimal@`:
|
||||
|
||||
- Updated to use `async` methods.
|
||||
|
||||
- `mongo-dev-server@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `mongo-id@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `mongo-livedata@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `mongo@2.0.0`:
|
||||
|
||||
- Updated to unify methods, `update`,`insert`,`remove`, `fetch` are now async, they are
|
||||
the same as their `*Async` counterpart.
|
||||
- `ensureIndex` and `createIndex` are now async.
|
||||
|
||||
- `npm-mongo@5.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `oauth-encryption@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `oauth@`:
|
||||
|
||||
- `_endOfPopupResponseTemplate` and `_endOfRedirectResponseTemplate` are no longer a property but now a function that returns a promise of the same value as before
|
||||
- the following server methods are now async:
|
||||
- `OAuth._renderOauthResults`
|
||||
- `OAuth._endOfLoginResponse`
|
||||
- `OAuth.renderEndOfLoginResponse`
|
||||
- `OAuth._storePendingCredential`
|
||||
- `OAuth._retrievePendingCredential`
|
||||
- `ensureConfigured`
|
||||
- `_cleanStaleResults`
|
||||
|
||||
- `oauth@3.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `oauth1@`:
|
||||
|
||||
- the following server methods are now async:
|
||||
- `OAuth._storeRequestToken`
|
||||
- `OAuth._retrieveRequestToken`
|
||||
|
||||
- `oauth1@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `oauth2@`:
|
||||
|
||||
- `OAuth._requestHandlers['2']` is now async.
|
||||
|
||||
- `oauth2@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `ordered-dict@2.0.0`:
|
||||
|
||||
- Added `forEachAsync` method.
|
||||
|
||||
- `package-stats-opt-out@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `package-version-parser@4.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `promise@1.0.0`:
|
||||
|
||||
- Removed `fibers` usage
|
||||
|
||||
- `random@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `rate-limit@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `reactive-dict@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `reactive-var@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `reload-safetybelt@2.0.0`:
|
||||
|
||||
- Added `ecmascript` package to `package.js`
|
||||
|
||||
- `reload@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `retry@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `routepolicy@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `server-render@1.0.0`:
|
||||
|
||||
- Updated usage with `getBoilerplate` that are now `async`.
|
||||
|
||||
- `service-configuration@2.0.0`:
|
||||
|
||||
- Updated to use `createIndexAsync`.
|
||||
|
||||
- `session@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `sha@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `shell-server@1.0.0`:
|
||||
|
||||
- Updated to handle promises results.
|
||||
|
||||
- `socket-stream-client@1.0.0`:
|
||||
|
||||
- Updated tests to handle `async` code.
|
||||
|
||||
- `spiderable@`:
|
||||
|
||||
- Updated handlers to use `handlers` that are now using express
|
||||
- removed `fibers` usage if flag is set to `true`
|
||||
|
||||
- `standard-minifier-css@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `standard-minifier-js@3.0.0`:
|
||||
|
||||
- `processFilesForBundle` is now `async`.
|
||||
|
||||
- `standard-minifiers@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `static-html@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `test-helpers@2.0.0`:
|
||||
|
||||
- Updated to use `async` methods.
|
||||
- Removed `fibers` usage.
|
||||
- Added possibliy to use `async` tests.
|
||||
|
||||
- `test-in-browser@2.0.0`:
|
||||
|
||||
- Updated css to be in dark mode.
|
||||
|
||||
- `test-in-console@2.0.0`:
|
||||
|
||||
- Updated log identation.
|
||||
|
||||
- `test-server-tests-in-console-once@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `tinytest-harness@1.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `tinytest@2.0.0`:
|
||||
|
||||
- Added `test name` to logs.
|
||||
- Removed `fibers` usage.
|
||||
|
||||
- `twitter-config-ui@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `twitter-oauth@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `typescript@5.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `underscore-tests@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `underscore@2.0.0`:
|
||||
|
||||
- Removed dependency in meteor package.
|
||||
|
||||
- `url@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `webapp-hashing@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `webapp@2.0.0`:
|
||||
|
||||
- These methods are now async:
|
||||
|
||||
- `WebAppInternals.reloadClientPrograms()`
|
||||
- `WebAppInternals.pauseClient()`
|
||||
- `WebAppInternals.generateClientProgram()`
|
||||
- `WebAppInternals.generateBoilerplate()`
|
||||
- `WebAppInternals.setInlineScriptsAllowed()`
|
||||
- `WebAppInternals.enableSubresourceIntegrity()`
|
||||
- `WebAppInternals.setBundledJsCssUrlRewriteHook()`
|
||||
- `WebAppInternals.setBundledJsCssPrefix()`
|
||||
- `WebAppInternals.getBoilerplate`
|
||||
|
||||
- Changed engine from connect to express and changed api naming to match express. See below:
|
||||
- `WebApp.connectHandlers.use(middleware)` is now `WebApp.handlers.use(middleware)`
|
||||
- `WebApp.rawConnectHandlers.use(middleware)` is now `WebApp.rawHandlers.use(middleware)`
|
||||
- `WebApp.connectApp` is now `WebApp.expressApp`
|
||||
|
||||
- `weibo-config-ui@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
- `weibo-oauth@2.0.0`:
|
||||
|
||||
- Package was bumped due to a dependency update. No code changes were made.
|
||||
|
||||
#### New Public API
|
||||
|
||||
|
||||
|
||||
- `accounts-base`: (2.9+)
|
||||
|
||||
- `Meteor.userAsync()`
|
||||
|
||||
- `callback-hook`:forEachAsync
|
||||
|
||||
- `forEachAsync`
|
||||
|
||||
- `ddp-server`: (2.8+)
|
||||
|
||||
- `Meteor.callAsync()`
|
||||
|
||||
- `minifier-css`: (2.9+)
|
||||
|
||||
- `CssTools.minifyCssAsync()`
|
||||
|
||||
- `mongo`:
|
||||
|
||||
- `Mongo.Collection`: (2.8+)
|
||||
- `createCappedCollectionAsync`
|
||||
- `createIndexAsync`
|
||||
- `dropCollectionAsync`
|
||||
- `dropIndexAsync`
|
||||
- `findOneAsync`
|
||||
- `insertAsync`
|
||||
- `removeAsync`
|
||||
- `updateAsync`
|
||||
- `upsertAsync`
|
||||
- `Collection.Cursor`: (2.8+)
|
||||
- `countAsync`
|
||||
- `fetchAsync`
|
||||
- `forEachAsync`
|
||||
- `mapAsync`
|
||||
- `[Symbol.asyncIterator]` so this code should work:
|
||||
```js
|
||||
for await (const document of collection.find(query, options)) /* ... */
|
||||
```
|
||||
|
||||
#### Internal API changes
|
||||
|
||||
`accounts-base`:
|
||||
|
||||
- `_attemptLogin`
|
||||
- `_loginMethod`
|
||||
- `_runLoginHandlers`
|
||||
|
||||
* Upgraded `node-gyp` to v9.4.0
|
||||
* Upgraded `node-pre-gyp` to `@mapbox/node-pre-gyp` v1.0.11
|
||||
|
||||
#### New Internal API
|
||||
|
||||
`accounts-password`:
|
||||
|
||||
- `Accounts._checkPasswordAsync`
|
||||
|
||||
#### Special thanks to
|
||||
|
||||
- [@StorytellerCZ](https://github.com/sponsors/StorytellerCZ/)
|
||||
|
||||
For making this great framework even better!
|
||||
@@ -0,0 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
This is a complete history of changes for Meteor releases.
|
||||
|
||||
[//]: # (Do not edit this file by hand.)
|
||||
|
||||
[//]: # (This is a generated file.)
|
||||
|
||||
[//]: # (If you want to change something in this file)
|
||||
|
||||
[//]: # (go to meteor/docs/generators/changelog/docs)
|
||||
|
||||
|
||||
|
||||
11
v3-docs/docs/generators/codegen.js
Normal file
11
v3-docs/docs/generators/codegen.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const { generateChangelog } = require("./changelog/script.js");
|
||||
const { listPackages } = require("./packages-listing/script.js");
|
||||
|
||||
async function main() {
|
||||
console.log("🚂 Started codegen 🚂");
|
||||
await generateChangelog();
|
||||
await listPackages();
|
||||
console.log("🚀 Done codegen 🚀");
|
||||
}
|
||||
|
||||
main();
|
||||
18
v3-docs/docs/generators/packages-listing/README.md
Normal file
18
v3-docs/docs/generators/packages-listing/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Listing of all meteor core packages
|
||||
|
||||
This is a script that will generate a list of all meteor core packages, being ran every build.
|
||||
This ensures that we always have a list of core packages up to date with their correct links to GitHub.
|
||||
|
||||
|
||||
We can always add packages to the list by adding them to the `script.js` constant `OUTSIDE_OF_CORE_PACKAGES`.
|
||||
|
||||
Should follow the following format:
|
||||
|
||||
```js
|
||||
{
|
||||
name: 'package-name',
|
||||
link: 'https://link-to-github.com/meteor/meteor/tree/devel/packages/package-name'
|
||||
}
|
||||
```
|
||||
|
||||
At the end, this script will update the file located in `docs/source/packages/packages-listing.md` with the new list of packages.
|
||||
55
v3-docs/docs/generators/packages-listing/script.js
Normal file
55
v3-docs/docs/generators/packages-listing/script.js
Normal file
@@ -0,0 +1,55 @@
|
||||
const fs = require("fs");
|
||||
const HEADER_TEMPLATE = `
|
||||
|
||||
[//]: # (Do not edit this file by hand.)
|
||||
|
||||
[//]: # (This is a generated file.)
|
||||
|
||||
[//]: # (If you want to change something in this file)
|
||||
|
||||
[//]: # (go to meteor/docs/generators/packages-listing)
|
||||
|
||||
# Core Packages
|
||||
|
||||
|
||||
`;
|
||||
|
||||
const OUTSIDE_OF_CORE_PACKAGES = [
|
||||
{
|
||||
name: "blaze",
|
||||
link: "https://github.com/meteor/blaze",
|
||||
},
|
||||
{
|
||||
name: "react-packages",
|
||||
link: "https://github.com/meteor/react-packages",
|
||||
},
|
||||
];
|
||||
|
||||
const IGNORED = ["depracated", "non-core"];
|
||||
const getPackages = () => {
|
||||
const packages = fs
|
||||
.readdirSync("../../packages", { withFileTypes: true })
|
||||
.filter((dirent) => dirent.isDirectory())
|
||||
.map((dirent) => dirent.name)
|
||||
.filter((name) => !IGNORED.includes(name))
|
||||
.map((name) => {
|
||||
return {
|
||||
name,
|
||||
link: `https://github.com/meteor/meteor/tree/devel/packages/${name}`,
|
||||
};
|
||||
});
|
||||
return [...OUTSIDE_OF_CORE_PACKAGES, ...packages];
|
||||
};
|
||||
|
||||
const generateMarkdown = (packages) =>
|
||||
packages.map(({ name, link }) => `### [${name}](${link}) {#${name}}`).join("\n");
|
||||
|
||||
exports.listPackages = function() {
|
||||
console.log("🚂 Started listing 🚂");
|
||||
const packages = getPackages();
|
||||
const markdown = generateMarkdown(packages);
|
||||
const content = HEADER_TEMPLATE + markdown;
|
||||
console.log("📝 Writing to file 📝");
|
||||
fs.writeFileSync("./api/packages-listing.md", content);
|
||||
console.log("🚀 Done package listing 🚀");
|
||||
}
|
||||
2951
v3-docs/docs/history.md
Normal file
2951
v3-docs/docs/history.md
Normal file
File diff suppressed because it is too large
Load Diff
52
v3-docs/docs/index.md
Normal file
52
v3-docs/docs/index.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
# https://vitepress.dev/reference/default-theme-home-page
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
text: "Meteor Docs"
|
||||
tagline: "All the documentation you need to build and deploy your app with Meteor."
|
||||
image:
|
||||
src: /logo.png
|
||||
alt: Meteor
|
||||
actions:
|
||||
- theme: brand
|
||||
text: View API Docs
|
||||
link: /about/what-is
|
||||
- theme: alt
|
||||
text: View on GitHub
|
||||
link: https://github.com/meteor/meteor
|
||||
|
||||
# TODO: Update with Docs features and links as they are added
|
||||
features:
|
||||
- title: Zero Config
|
||||
icon: 🛠️
|
||||
details: Use popular frameworks and tools right out-of-the-box. Focus on building features instead of configuring tools.
|
||||
|
||||
- title: Front-end Agnostic
|
||||
icon: 🎨
|
||||
details: Choose your preferred front-end framework like React, VueJS, Blaze, Svelte, or Solid.
|
||||
|
||||
- title: RPCs API
|
||||
icon: 📡
|
||||
details: Easily connect back-end and front-end with Methods. Our Remote Procedure Call (RPC) system
|
||||
|
||||
- title: Easy to Deploy
|
||||
icon: 🚀
|
||||
details: Deploy using one command from the CLI or directly from your Git repository by using Meteor Cloud.
|
||||
|
||||
- title: Real-time Features
|
||||
icon: ⚡
|
||||
details: Develop apps with real-time features like chats and collaborative apps with ease by using publications and subscriptions.
|
||||
|
||||
- title: TypeScript Support
|
||||
icon: 📝
|
||||
details: Experience the power of TypeScript inference to boost productivity for your full-stack application.
|
||||
|
||||
- title: Built-in Accounts
|
||||
icon: 👤
|
||||
details: Login and Accounts package ready to use with your app. Never rebuilt an authentication system again.
|
||||
|
||||
- title: Pioneering and Reliable
|
||||
icon: 🏆
|
||||
details: Developed for over a decade and trusted by industry giants. Alows you to build and scale efficiently.
|
||||
---
|
||||
165
v3-docs/docs/jsdoc/docdata-jsdoc-template/publish.js
Normal file
165
v3-docs/docs/jsdoc/docdata-jsdoc-template/publish.js
Normal file
@@ -0,0 +1,165 @@
|
||||
/*global require: true */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
// This file receives data from JSDoc via the `publish` exported function,
|
||||
// and converts it into JSON that is written to a file.
|
||||
|
||||
var fs = require('jsdoc/fs');
|
||||
var helper = require('jsdoc/util/templateHelper');
|
||||
|
||||
var stringify = require("canonical-json");
|
||||
|
||||
// This is the big map of name -> data that we'll write to a file.
|
||||
var dataContents = {};
|
||||
// List of just the names, which we'll also write to a file.
|
||||
var names = [];
|
||||
|
||||
/**
|
||||
* Get a tag dictionary from the tags field on the object, for custom fields
|
||||
* like package
|
||||
* @param {JSDocData} data The thing you get in the TaffyDB from JSDoc
|
||||
* @return {Object} Keys are the parameter names, values are the values.
|
||||
*/
|
||||
var getTagDict = function (data) {
|
||||
var tagDict = {};
|
||||
|
||||
if (data.tags) {
|
||||
data.tags.forEach(function (tag) {
|
||||
tagDict[tag.title] = tag.value;
|
||||
});
|
||||
}
|
||||
|
||||
return tagDict;
|
||||
};
|
||||
|
||||
// Fix up a JSDoc entry and add it to `dataContents`.
|
||||
var addToData = function (entry) {
|
||||
Object.assign(entry, getTagDict(entry));
|
||||
|
||||
// strip properties we don't want
|
||||
entry.comment = undefined;
|
||||
entry.___id = undefined;
|
||||
entry.___s = undefined;
|
||||
entry.tags = undefined;
|
||||
|
||||
// generate `.filepath` and `.lineno` from `.meta`
|
||||
if (entry.meta && entry.meta.path) {
|
||||
var packagesFolder = 'packages/';
|
||||
var index = entry.meta.path.indexOf(packagesFolder);
|
||||
if (index != -1 && !entry.isprototype) {
|
||||
var fullFilePath = entry.meta.path.substr(index + packagesFolder.length) + '/' + entry.meta.filename;
|
||||
entry.filepath = fullFilePath;
|
||||
entry.lineno = entry.meta.lineno;
|
||||
}
|
||||
}
|
||||
|
||||
entry.meta = undefined;
|
||||
|
||||
if (!entry.importfrompackage && entry.filepath) {
|
||||
entry.module = entry.filepath.split('/')[0];
|
||||
} else {
|
||||
entry.module = entry.importfrompackage;
|
||||
}
|
||||
|
||||
names.push(entry.longname);
|
||||
dataContents[entry.longname] = entry;
|
||||
};
|
||||
|
||||
/**
|
||||
Entry point where JSDoc calls us. It passes us data in the form of
|
||||
a TaffyDB object (which is an in-JS database of sorts that you can
|
||||
query for records.
|
||||
|
||||
@param {TAFFY} taffyData See <http://taffydb.com/>.
|
||||
@param {object} opts
|
||||
@param {Tutorial} tutorials
|
||||
*/
|
||||
exports.publish = function(taffyData) {
|
||||
var data = helper.prune(taffyData);
|
||||
|
||||
var namespaces = helper.find(data, {kind: "namespace"});
|
||||
|
||||
// prepare all of the namespaces
|
||||
namespaces.forEach(function (namespace) {
|
||||
if (namespace.summary) {
|
||||
addToData(namespace);
|
||||
}
|
||||
});
|
||||
|
||||
var properties = helper.find(data, {kind: "member"});
|
||||
|
||||
properties.forEach(function (property) {
|
||||
if (property.summary) {
|
||||
addToData(property);
|
||||
}
|
||||
});
|
||||
|
||||
// Callback descriptions are going to be embedded into Function descriptions
|
||||
// when they are used as arguments, so we always attach them to reference
|
||||
// them later.
|
||||
var callbacks = helper.find(data, {kind: "typedef"});
|
||||
callbacks.forEach(function (cb) {
|
||||
delete cb.comment;
|
||||
addToData(cb);
|
||||
});
|
||||
|
||||
var functions = helper.find(data, {kind: "function"});
|
||||
var constructors = helper.find(data, {kind: "class"});
|
||||
|
||||
// we want to do all of the same transformations to classes and functions
|
||||
functions = functions.concat(constructors);
|
||||
|
||||
// insert all of the function data into the namespaces
|
||||
functions.forEach(function (func) {
|
||||
if (! func.summary) {
|
||||
// we use the @summary tag to indicate that an item is documented
|
||||
return;
|
||||
}
|
||||
|
||||
func.options = [];
|
||||
var filteredParams = [];
|
||||
|
||||
// Starting a param with `options.` makes it an option, not a
|
||||
// param. Dot (`.`) in this case binds tighter than comma, so
|
||||
// `options.foo,bar` will create an option named `foo, bar`
|
||||
// (representing two options in the docs). We process pipes so
|
||||
// that `options.foo|bar` also results in `foo, bar`.
|
||||
(func.params || []).forEach(function (param) {
|
||||
param.name = param.name.replace(/,|\|/g, ", ");
|
||||
|
||||
var splitName = param.name.split(".");
|
||||
|
||||
if (splitName.length < 2 || splitName[0] !== "options") {
|
||||
// not an option
|
||||
filteredParams.push(param);
|
||||
return;
|
||||
}
|
||||
|
||||
param.name = splitName[1];
|
||||
|
||||
func.options.push(param);
|
||||
});
|
||||
|
||||
func.params = filteredParams;
|
||||
|
||||
// the entire unparsed doc comment. takes up too much room in the
|
||||
// data file.
|
||||
delete func.comment;
|
||||
|
||||
addToData(func);
|
||||
});
|
||||
|
||||
// write full docs JSON
|
||||
var jsonString = stringify(dataContents, null, 2);
|
||||
var jsString = "export default" + jsonString + ";";
|
||||
jsString = "// This file is automatically generated by JSDoc; regenerate it with scripts/admin/jsdoc/jsdoc.sh\n" + jsString;
|
||||
var docsDataFilename = "./v3-docs/docs/data/data.js";
|
||||
fs.writeFileSync(docsDataFilename, jsString);
|
||||
|
||||
// write name tree JSON
|
||||
jsonString = stringify(names.sort(), null, 2);
|
||||
var nameTreeFilename= "./v3-docs/docs/data/names.json";
|
||||
fs.writeFileSync(nameTreeFilename, jsonString);
|
||||
};
|
||||
})();
|
||||
22
v3-docs/docs/jsdoc/jsdoc-conf.json
Normal file
22
v3-docs/docs/jsdoc/jsdoc-conf.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"plugins": ["plugins/markdown"],
|
||||
"markdown": {
|
||||
"parser": "gfm"
|
||||
},
|
||||
"source": {
|
||||
"exclude": [
|
||||
"packages/ddp/sockjs-0.3.4.js",
|
||||
"packages/test-in-browser/diff_match_patch_uncompressed.js",
|
||||
"packages/jquery/jquery.js",
|
||||
"packages/underscore/underscore.js",
|
||||
"packages/json/json2.js",
|
||||
"packages/minimongo/minimongo_tests.js",
|
||||
"tools/node_modules",
|
||||
"tools/skel-pack/package.js",
|
||||
"docs",
|
||||
"guide",
|
||||
"**/node_modules",
|
||||
"npm-packages"
|
||||
]
|
||||
}
|
||||
}
|
||||
19
v3-docs/docs/jsdoc/jsdoc.sh
Executable file
19
v3-docs/docs/jsdoc/jsdoc.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
INFINITY=10000
|
||||
|
||||
TOPDIR=$(pwd)
|
||||
METEOR_DIR="../../"
|
||||
cd "$METEOR_DIR"
|
||||
|
||||
# Ensure that jsdoc failure actually makes this script fail.
|
||||
set -o pipefail
|
||||
|
||||
# Call git grep to find all js files with the appropriate comment tags,
|
||||
# and only then pass it to JSDoc which will parse the JS files.
|
||||
# This is a whole lot faster than calling JSDoc recursively.
|
||||
git grep -ialE "@(summary|borrows|namespace|memberof|alias)" | xargs -L ${INFINITY} -t \
|
||||
"$TOPDIR/node_modules/.bin/jsdoc" \
|
||||
-t "$TOPDIR/jsdoc/docdata-jsdoc-template" \
|
||||
-c "$TOPDIR/jsdoc/jsdoc-conf.json" \
|
||||
2>&1 | grep -v 'WARNING: JSDoc does not currently handle'
|
||||
2282
v3-docs/docs/package-lock.json
generated
Normal file
2282
v3-docs/docs/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
v3-docs/docs/package.json
Normal file
18
v3-docs/docs/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"scripts": {
|
||||
"generate-jsdoc": "jsdoc/jsdoc.sh",
|
||||
"codegen": "jsdoc/jsdoc.sh && node ./generators/codegen.js",
|
||||
"docs:dev": "npm run codegen && vitepress dev",
|
||||
"docs:build": "npm run codegen && vitepress build",
|
||||
"docs:preview": "vitepress preview",
|
||||
"deploy:preview": "npm run docs:build && npm run docs:preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"canonical-json": "^0.0.4",
|
||||
"jsdoc": "^4.0.2",
|
||||
"vitepress": "1.0.0-rc.31"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue-collapsed": "^1.3.0"
|
||||
}
|
||||
}
|
||||
222
v3-docs/docs/packages/accounts-2fa.md
Normal file
222
v3-docs/docs/packages/accounts-2fa.md
Normal file
@@ -0,0 +1,222 @@
|
||||
# accounts-2fa
|
||||
|
||||
This package allows you to provide a way for your users to enable 2FA on their accounts, using an authenticator app such as Google Authenticator, or 1Password. When the user is logged in on your app, they will be able to generate a new QR code and read this code on the app they prefer. After that, they'll start receiving their codes. Then, they can finish enabling 2FA on your app, and every time they try to log in to your app, you can redirect them to a place where they can provide a code they received from the authenticator.
|
||||
|
||||
To provide codes that are exactly compatible with all other Authenticator apps and services that implements TOTP, this package uses [node-2fa](https://www.npmjs.com/package/node-2fa) which works on top of [notp](https://github.com/guyht/notp), **that** implements TOTP ([RFC 6238](https://www.ietf.org/rfc/rfc6238.txt)) (the Authenticator standard), which is based on HOTP ([RFC 4226](https://www.ietf.org/rfc/rfc4226.txt)).
|
||||
|
||||
> This package is meant to be used with [`accounts-password`](../api/accounts.md#passwords) or [`accounts-passwordless`](./accounts-passwordless.md), so if you don't have either of those in your project, you'll need to add one of them. In the future, we want to enable the use of this package with other login methods, our oauth methods (Google, GitHub, etc...).
|
||||
|
||||
## 2FA Activation Flow {#activating-2fa}
|
||||
|
||||
The first step, in order to enable 2FA, is to generate a QR code so that the user can scan it in an authenticator app and start receiving codes.
|
||||
|
||||
<ApiBox name="Accounts.generate2faActivationQrCode" from="accounts-base"/>
|
||||
|
||||
Receives an `appName` which is the name of your app that will show up when the user scans the QR code. Also, a callback called, on success, with a QR code in SVG format, a QR secret, and the URI that can be used to activate the 2FA in an authenticator app,
|
||||
or a single `Error` argument on failure.
|
||||
|
||||
On success, this function will also add an object to the logged user's services object containing the QR secret:
|
||||
|
||||
```js
|
||||
services: {
|
||||
...
|
||||
twoFactorAuthentication: {
|
||||
secret: "***"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here it's an example on how to call this function:
|
||||
|
||||
```js
|
||||
import { Buffer } from "buffer";
|
||||
import { Accounts } from 'meteor/accounts-base';
|
||||
|
||||
|
||||
// component
|
||||
const [qrCode, setQrCode] = useState(null);
|
||||
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
Accounts.generate2faActivationQrCode("My app name", (err, result) => {
|
||||
if (err) {console.error("...", err);return;}
|
||||
const { svg, secret, uri } = result;
|
||||
/*
|
||||
the svg can be converted to base64, then be used like:
|
||||
<img
|
||||
width="200"
|
||||
src={`data:image/svg+xml;base64,${qrCode}`}
|
||||
/>
|
||||
*/
|
||||
setQrCode(Buffer.from(svg).toString('base64'));
|
||||
})
|
||||
}}
|
||||
>
|
||||
Generate a new code
|
||||
</button>
|
||||
```
|
||||
|
||||
This method can fail throwing the following error:
|
||||
|
||||
- "The 2FA is activated. You need to disable the 2FA first before trying to generate a new activation code [2fa-activated]" if trying to generate an activation when the user already have 2FA enabled.
|
||||
|
||||
At this point, the 2FA won't be activated just yet. Now that the user has access to the codes generated by their authenticator app, you can call the function `Accounts.enableUser2fa`:
|
||||
|
||||
<ApiBox name="Accounts.enableUser2fa" from="accounts-base"/>
|
||||
|
||||
It should be called with a code that the users will receive from the authenticator app once they read the QR code. The callback is called with a single `Error` argument on failure. If the code provided is correct, a `type` will be added to the user's `twoFactorAuthentication` object and now 2FA is considered enabled:
|
||||
|
||||
```js
|
||||
services: {
|
||||
...
|
||||
twoFactorAuthentication: {
|
||||
type: "otp",
|
||||
secret: "***",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To verify whether or not a user has 2FA enabled, you can call the function `Accounts.has2faEnabled`:
|
||||
|
||||
<ApiBox name="Accounts.has2faEnabled" from="accounts-base"/>
|
||||
|
||||
This function must be called when the user is logged in.
|
||||
|
||||
|
||||
## Disabling 2FA {#disabling-2fa}
|
||||
|
||||
To disable 2FA for a user use this method:
|
||||
|
||||
<ApiBox name="Accounts.disableUser2fa" from="accounts-base"/>
|
||||
|
||||
To call this function the user must be already logged in.
|
||||
|
||||
## Log in with 2FA {#log-in-with-2fa}
|
||||
|
||||
Now that you have a way to allow your users to enable 2FA on their accounts, you can create a login flow based on that.
|
||||
|
||||
As said at the beginning of this guide, this package is currently working with two other packages: `accounts-password` and `accounts-passwordless`. Below there is an explanation on how to use this package with them.
|
||||
|
||||
|
||||
## Working with accounts-password {#working-with-accounts-password}
|
||||
|
||||
When calling the function `Meteor.loginWithPassword`, if the 2FA is enabled for the user, an error will be returned to the callback, so you can redirect the user to a place where they can provide a code.
|
||||
|
||||
As an example:
|
||||
|
||||
```js
|
||||
<button
|
||||
onClick={() => {
|
||||
Meteor.loginWithPassword(username, password, (error) => {
|
||||
if (error) {
|
||||
if (error.error === "no-2fa-code") {
|
||||
// send user to a page or show a component
|
||||
// where they can provide a 2FA code
|
||||
setShouldAskCode(true);
|
||||
return;
|
||||
}
|
||||
console.error("Error trying to log in (user without 2fa)", error);
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
```
|
||||
|
||||
If the 2FA is not enabled, the user will be logged in normally.
|
||||
|
||||
The function you will need to call now to allow the user to login is `Meteor.loginWithPasswordAnd2faCode`:
|
||||
|
||||
<ApiBox name="Meteor.loginWithPasswordAnd2faCode"/>
|
||||
|
||||
Now you will be able to receive a code from the user and this function will verify if the code is valid. If it is, the user will be logged in.
|
||||
|
||||
So the call of this function should look something like this:
|
||||
|
||||
```js
|
||||
<button
|
||||
onClick={() => {
|
||||
Meteor.loginWithPasswordAnd2faCode(username, password, code, (error) => {
|
||||
if (error) {
|
||||
console.error("Error trying to log in (user with 2fa)", error);
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
Validate and log in
|
||||
</button>
|
||||
```
|
||||
|
||||
This method can fail throwing one of the following errors:
|
||||
|
||||
- "2FA code must be informed [no-2fa-code]" if a 2FA code was not provided.
|
||||
- "Invalid 2FA code [invalid-2fa-code]" if the provided 2FA code is invalid.
|
||||
|
||||
|
||||
## Working with accounts-passwordless {#working-with-accounts-passwordless}
|
||||
|
||||
Following the same logic from the previous package, if the 2FA is enabled, an error will be returned to the callback of the function
|
||||
[`Meteor.passwordlessLoginWithToken`](./accounts-passwordless.md#Meteor-passwordlessLoginWithToken),
|
||||
then you can redirect the user to a place where they can provide a code.
|
||||
|
||||
Here is an example:
|
||||
|
||||
```js
|
||||
<button
|
||||
onClick={() => {
|
||||
// logging in just with token
|
||||
Meteor.passwordlessLoginWithToken(email, token, (error) => {
|
||||
if (error) {
|
||||
if (error.error === "no-2fa-code") {
|
||||
// send user to a page or show a component
|
||||
// where they can provide a 2FA code
|
||||
setShouldAskCode(true);
|
||||
return;
|
||||
}
|
||||
console.error("Error verifying token", error);
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
Validate token
|
||||
</button>
|
||||
```
|
||||
|
||||
Now you can call the function `Meteor.passwordlessLoginWithTokenAnd2faCode` that will allow you to provide a selector, token, and 2FA code:
|
||||
|
||||
<ApiBox name="Meteor.passwordlessLoginWithTokenAnd2faCode"/>
|
||||
|
||||
This method can fail throwing one of the following errors:
|
||||
|
||||
- "2FA code must be informed [no-2fa-code]" if a 2FA code was not provided.
|
||||
- "Invalid 2FA code [invalid-2fa-code]" if the provided 2FA code is invalid.
|
||||
|
||||
## Integrating an Authentication Package with accounts-2fa {#integrating-auth-package}
|
||||
|
||||
To integrate this package with any other existing Login method, it's necessary following two steps:
|
||||
|
||||
1 - For the client, create a new method from your current login method. So for example, from the method `Meteor.loginWithPassword` we created a new one called `Meteor.loginWithPasswordAnd2faCode`, and the only difference between them is that the latest one receives one additional parameter, the 2FA code, but we call the same function on the server side.
|
||||
|
||||
2 - For the server, inside the function that will log the user in, you verify if the function `Accounts._check2faEnabled` exists, and if yes, you call it providing the user object you want to check if the 2FA is enabled, and if either of these statements are false, you proceed with the login flow. This function exists only when the package `accounts-2fa` is added to the project.
|
||||
|
||||
If both statements are true, and the login validation succeeds, you verify if a code was provided: if not, throw an error; if it was provided, verify if the code is valid by calling the function `Accounts._isTokenValid`. if `Accounts._isTokenValid` returns false, throw an error.
|
||||
|
||||
Here it's an example:
|
||||
|
||||
```js
|
||||
const result = validateLogin();
|
||||
if (!result.error && Accounts._check2faEnabled?.(user)) {
|
||||
if (!code) {
|
||||
Accounts._handleError("2FA code must be informed.");
|
||||
}
|
||||
if (
|
||||
!Accounts._isTokenValid(user.services.twoFactorAuthentication.secret, code)
|
||||
) {
|
||||
Accounts._handleError("Invalid 2FA code.");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
```
|
||||
39
v3-docs/docs/packages/accounts-passwordless.md
Normal file
39
v3-docs/docs/packages/accounts-passwordless.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Passwordless
|
||||
|
||||
Passwordless package allows you to create a login for users without the need for user to provide password. Upon registering or login an email is sent to the user's email with a code to enter to confirm login and a link to login directly. Since the user is responding to the email it will also verify the email.
|
||||
|
||||
The first step to in the passwordless process is for the user to sign-up or request a token to their email address. You can do that with the following:
|
||||
<ApiBox name="Accounts.requestLoginTokenForUser" from="accounts-base"/>
|
||||
|
||||
If the user is signing up you can pass in the `userData` object like in [Accounts.createUser](/api/accounts#Accounts-createUser).
|
||||
|
||||
<ApiBox name="Meteor.passwordlessLoginWithToken" />
|
||||
The second step in the passwordless flow. Like all the other `loginWith` functions call this method to login the user with the token they have inputted.
|
||||
|
||||
<ApiBox name="Accounts.sendLoginTokenEmail" from="accounts-base" />
|
||||
Use this function if you want to manually send the email to users to login with token from the server. Do note that you will need to create the token/sequence and save it in the DB yourself. This is good if you want to change how the tokens look or are generated, but unless you are sure of what you are doing we don't recommend it.
|
||||
|
||||
<h3 id="config-options">Settings Options</h3>
|
||||
|
||||
You can use the function `Accounts.config` in the server to change some settings on this package:
|
||||
|
||||
- **tokenSequenceLength**: use `Accounts.config({tokenSequenceLength: _Number_})` to the size of the token sequence generated. The default is 6.
|
||||
|
||||
- **loginTokenExpirationHours**: use `Accounts.config({loginTokenExpirationHours: _Number_})` to set the amount of time a token sent is valid. As it's just a number, you can use, for example, 0.5 to make the token valid for just half hour. The default is 1 hour.
|
||||
|
||||
<h3 id="passwordless-email-templates">E-mail templates</h3>
|
||||
|
||||
`accounts-passwordless` brings new templates that you can edit to change the look of emails which send code to users. The email template is named `sendLoginToken` and beside `user` and `url`, the templates also receive a data object with `sequence` which is the user's code.
|
||||
|
||||
```javascript
|
||||
sendLoginToken: {
|
||||
text: (user, url, { sequence }) => {
|
||||
/* text template */
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
<h3 id="enabling-2fa">Enable 2FA for this package</h3>
|
||||
|
||||
You can add 2FA to your login flow by using the package [accounts-2fa](./accounts-2fa.md).
|
||||
You can find an example showing how this would look like [here](./accounts-2fa.md#working-with-accounts-passwordless).
|
||||
29
v3-docs/docs/packages/accounts-ui.md
Normal file
29
v3-docs/docs/packages/accounts-ui.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# accounts-ui
|
||||
|
||||
A turn-key user interface for Meteor Accounts.
|
||||
|
||||
To add Accounts and a set of login controls to an application, add the
|
||||
`accounts-ui` package and at least one login provider package:
|
||||
`accounts-password`, `accounts-facebook`, `accounts-github`,
|
||||
`accounts-google`, `accounts-twitter`, or `accounts-weibo`.
|
||||
|
||||
Then simply add the <span v-pre>`{{> loginButtons}}`</span> helper to an HTML file. This
|
||||
will place a login widget on the page. If there is only one provider configured
|
||||
and it is an external service, this will add a login/logout button. If you use
|
||||
`accounts-password` or use multiple external login services, this will add
|
||||
a "Sign in" link which opens a dropdown menu with login options. If you plan to
|
||||
position the login dropdown in the right edge of the screen, use
|
||||
<span v-pre>`{{> loginButtons align="right"}}`</span> in order to get the dropdown to lay
|
||||
itself out without expanding off the edge of the screen.
|
||||
|
||||
To configure the behavior of <span v-pre>`{{> loginButtons}}`</span>, use
|
||||
[`Accounts.ui.config`](../api/accounts.md#loggingIn).
|
||||
|
||||
`accounts-ui` also includes modal popup dialogs to handle links from
|
||||
[`sendResetPasswordEmail`](../api/accounts.md#Accounts-sendResetPasswordEmail),
|
||||
[`sendVerificationEmail`](../api/accounts.md#Accounts-sendVerificationEmail),
|
||||
and [`sendEnrollmentEmail`](../api/accounts.md#Accounts-sendEnrollmentEmail). These
|
||||
do not have to be manually placed in HTML: they are automatically activated
|
||||
when the URLs are loaded.
|
||||
|
||||
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.
|
||||
111
v3-docs/docs/packages/appcache.md
Normal file
111
v3-docs/docs/packages/appcache.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# AppCache
|
||||
|
||||
> This package has been deprecated since [applicationCache](https://developer.mozilla.org/en-US/docs/Web/API/Window/applicationCache), which this package relies on, has been deprecated and is not available on the latest browsers. Plaese consider using [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) as an replacement.
|
||||
|
||||
The `appcache` package stores the static parts of a Meteor application
|
||||
(the client side Javascript, HTML, CSS, and images) in the browser's
|
||||
[application cache](https://en.wikipedia.org/wiki/AppCache). To enable
|
||||
caching simply add the `appcache` package to your project.
|
||||
|
||||
* Once a user has visited a Meteor application for the first time and
|
||||
the application has been cached, on subsequent visits the web page
|
||||
loads faster because the browser can load the application out of the
|
||||
cache without contacting the server first.
|
||||
|
||||
* Hot code pushes are loaded by the browser in the background while the
|
||||
app continues to run. Once the new code has been fully loaded the
|
||||
browser is able to switch over to the new code quickly.
|
||||
|
||||
* The application cache allows the application to be loaded even when
|
||||
the browser doesn't have an Internet connection, and so enables using
|
||||
the app offline.
|
||||
|
||||
(Note however that the `appcache` package by itself doesn't make
|
||||
*data* available offline: in an application loaded offline, a Meteor
|
||||
Collection will appear to be empty in the client until the Internet
|
||||
becomes available and the browser is able to establish a DDP
|
||||
connection).
|
||||
|
||||
To turn AppCache off for specific browsers use:
|
||||
|
||||
```js
|
||||
Meteor.AppCache.config({
|
||||
chrome: false,
|
||||
firefox: false
|
||||
});
|
||||
```
|
||||
|
||||
The supported browsers that can be enabled or disabled include, but are
|
||||
not limited to, `android`, `chrome`, `chromium`, `chromeMobileIOS`,
|
||||
`firefox`, `ie`, `mobileSafari` and `safari`.
|
||||
|
||||
Browsers limit the amount of data they will put in the application
|
||||
cache, which can vary due to factors such as how much disk space is
|
||||
free. Unfortunately if your application goes over the limit rather
|
||||
than disabling the application cache altogether and running the
|
||||
application online, the browser will instead fail that particular
|
||||
*update* of the cache, leaving your users running old code.
|
||||
|
||||
Thus it's best to keep the size of the cache below 5MB. The
|
||||
`appcache` package will print a warning on the Meteor server console
|
||||
if the total size of the resources being cached is over 5MB.
|
||||
|
||||
Starting from `appcache@1.2.5`, if you need more advanced logic
|
||||
to enable/disable the cache, you can use the `enableCallback` option
|
||||
that is evaluated on a per-request basis. For example:
|
||||
|
||||
```js
|
||||
// Enable offline mode using a value from database and certificate validation
|
||||
Meteor.AppCache.config({
|
||||
// This option is available starting from appcache@1.2.4
|
||||
enableCallback: () => {
|
||||
if (!getSettingsFromDb("public.appcache_enabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const validation = validateClientCert({
|
||||
clientCertPayload: req.headers["x-client-cert"],
|
||||
url: req.url.href,
|
||||
});
|
||||
|
||||
return validation.passed;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
If you have files too large to fit in the cache you can disable
|
||||
caching by URL prefix. For example,
|
||||
|
||||
```js
|
||||
Meteor.AppCache.config({ onlineOnly: ['/online/'] });
|
||||
```
|
||||
|
||||
causes files in your `public/online` directory to not be cached, and
|
||||
so they will only be available online. You can then move your large
|
||||
files into that directory and refer to them at the new URL:
|
||||
|
||||
```html
|
||||
<img src="/online/bigimage.jpg">
|
||||
```
|
||||
|
||||
If you'd prefer not to move your files, you can use the file names
|
||||
themselves as the URL prefix:
|
||||
|
||||
```js
|
||||
Meteor.AppCache.config({
|
||||
onlineOnly: [
|
||||
'/bigimage.jpg',
|
||||
'/largedata.json'
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
though keep in mind that since the exclusion is by prefix (this is a
|
||||
limitation of the application cache manifest), excluding
|
||||
`/largedata.json` will also exclude such URLs as
|
||||
`/largedata.json.orig` and `/largedata.json/file1`.
|
||||
|
||||
For more information about how Meteor interacts with the application
|
||||
cache, see the
|
||||
[AppCache page](https://github.com/meteor/meteor/wiki/AppCache)
|
||||
in the Meteor wiki.
|
||||
13
v3-docs/docs/packages/audit-argument-checks.md
Normal file
13
v3-docs/docs/packages/audit-argument-checks.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Audit Argument Checks
|
||||
|
||||
|
||||
This package causes Meteor to require that all arguments passed to methods and
|
||||
publish functions are [checked](../api/check.md). Any method that does not pass each
|
||||
one of its arguments to `check` will throw an error, which will be logged on the
|
||||
server and which will appear to the client as a
|
||||
`500 Internal server error`. This is a simple way to help ensure that your
|
||||
app has complete check coverage.
|
||||
|
||||
Methods and publish functions that do not need to validate their arguments can
|
||||
simply run `check(arguments, [Match.Any])` to satisfy the
|
||||
`audit-argument-checks` coverage checker.
|
||||
62
v3-docs/docs/packages/autoupdate.md
Normal file
62
v3-docs/docs/packages/autoupdate.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Autoupdate
|
||||
|
||||
This is the Meteor package that provides hot code push (HCP) functionality.
|
||||
|
||||
Every Meteor application that wasn't created with the `--minimal` option
|
||||
has this package already through `meteor-base` and HCP should work out of the
|
||||
box. For those running `--minimal` applications and want to benefit from this
|
||||
package, just add it with `meteor add autoupdate`.
|
||||
|
||||
> `autoupdate` adds up to 30KB on your client's production bundle.
|
||||
|
||||
With this package Meteor will use DDP to publish a collection called
|
||||
_'meteor_autoupdate_clientVersions'_. This collection will be subscribed by the
|
||||
user's client and every time the client identifies a change in the published
|
||||
version it will refresh itself.
|
||||
|
||||
## Browser Client
|
||||
|
||||
The refresh will happen in the browser in two different ways: a _soft update_,
|
||||
and a _hard update_. If Meteor identifies that only stylesheets were changed, then it
|
||||
will verify if the user's browser is capable of reloading CSS on the fly, and a
|
||||
soft update will take place. The soft update will replace the old stylesheet
|
||||
with the new stylesheet without triggering a full page reload.
|
||||
|
||||
In cases where a change in a server's or client's compiled file happens, the hard
|
||||
update will take place: Meteor will force a complete browser reload using the
|
||||
`reload` package.
|
||||
|
||||
> Among other things, the `reload` package tries do reload the application
|
||||
> preserving some unchanged cached files.
|
||||
|
||||
## Cordova Client
|
||||
|
||||
There is no soft update with Cordova apps, the client is always fully refreshed
|
||||
once a change is detected.
|
||||
|
||||
### `usesCleartextTraffic`
|
||||
Starting with Android 9 (API level 28), [cleartext support is disabled](https://developer.android.com/training/articles/security-config) by default.
|
||||
During development `autoupdate` uses cleartext to publish new client versions.
|
||||
If your app targets Android 9 or greater, it will be necessary to create a
|
||||
`mobile-config.js` file enabling the use of cleartext in order to have HCP working:
|
||||
|
||||
```js
|
||||
App.appendToConfig(`<edit-config file="app/src/main/AndroidManifest.xml"
|
||||
mode="merge"
|
||||
target="/manifest/application"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application android:usesCleartextTraffic="true"></application>
|
||||
</edit-config>
|
||||
`);
|
||||
```
|
||||
|
||||
### `--mobile-server`
|
||||
Additionally, for the HCP functionality to work it is also mandatory to provide
|
||||
the address for the application server with `--mobile-server` option. If you're
|
||||
testing your app on an emulator you should run it with `meteor run android --mobile-server 10.0.2.2:3000`.
|
||||
If you're running it on a real device, the application server and the device
|
||||
should be on the same network, and you should run your app with `meteor run android --mobile-server XXX.XXX.XXX.XXX`
|
||||
where *XXX.XXX.XXX.XXX* is your local development address, _e.g. 192.168.1.4_.
|
||||
|
||||
> To have a better understanding of how HCP works for mobile apps already
|
||||
> published to production refer to [Hot code push on mobile](https://guide.meteor.com/cordova.html#hot-code-push)
|
||||
175
v3-docs/docs/packages/browser-policy.md
Normal file
175
v3-docs/docs/packages/browser-policy.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# Browser Policy
|
||||
|
||||
The `browser-policy` family of packages, part of
|
||||
[Webapp](https://github.com/meteor/meteor/tree/master/packages/webapp), lets you
|
||||
set security-related policies that will be enforced by newer browsers. These
|
||||
policies help you prevent and mitigate common attacks like cross-site scripting
|
||||
and clickjacking.
|
||||
|
||||
## Details
|
||||
|
||||
When you add `browser-policy` to your app, you get default configurations for
|
||||
the HTTP headers X-Frame-Options and Content-Security-Policy. X-Frame-Options
|
||||
tells the browser which websites are allowed to frame your app. You should only
|
||||
let trusted websites frame your app, because malicious sites could harm your
|
||||
users with [clickjacking attacks](https://www.owasp.org/index.php/Clickjacking).
|
||||
[Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Security/CSP/Introducing_Content_Security_Policy)
|
||||
tells the browser where your app can load content from, which encourages safe
|
||||
practices and mitigates the damage of a cross-site-scripting attack.
|
||||
`browser-policy` also provides functions for you to configure these policies if
|
||||
the defaults are not suitable.
|
||||
|
||||
If you only want to use Content-Security-Policy or X-Frame-Options but not both,
|
||||
you can add the individual packages `browser-policy-content` or
|
||||
`browser-policy-framing` instead of `browser-policy`.
|
||||
|
||||
For most apps, we recommend that you take the following steps:
|
||||
|
||||
- Add `browser-policy` to your app to enable a starter policy. With this starter
|
||||
policy, your app's client code will be able to load content (images, scripts,
|
||||
fonts, etc.) only from its own origin, except that XMLHttpRequests and WebSocket
|
||||
connections can go to any origin. Further, your app's client code will not be
|
||||
able to use functions such as `eval()` that convert strings to code. Users'
|
||||
browsers will only let your app be framed by web pages on the same origin as
|
||||
your app.
|
||||
- You can use the functions described below to customize the policies. If your
|
||||
app does not need any inline Javascript such as inline `<script>` tags, we
|
||||
recommend that you modify the policy by calling
|
||||
`BrowserPolicy.content.disallowInlineScripts()` in server code. This will result
|
||||
in one extra round trip when your app is loaded, but will help prevent
|
||||
cross-site scripting attacks by disabling all scripts except those loaded from a
|
||||
`script src` attribute.
|
||||
|
||||
Meteor determines the browser policy when the server starts up, so you should
|
||||
call `BrowserPolicy` functions on the server in top-level application code or in
|
||||
`Meteor.startup`. `BrowserPolicy` functions cannot be used in client code.
|
||||
|
||||
## Usage
|
||||
|
||||
### Frame options
|
||||
|
||||
By default, if you add `browser-policy` or `browser-policy-framing`, only web
|
||||
pages on the same origin as your app are allowed to frame your app. You can use
|
||||
the following functions to modify this policy.
|
||||
|
||||
## BrowserPolicy.framing.disallow()
|
||||
|
||||
Your app will never render inside a frame or iframe.
|
||||
|
||||
## BrowserPolicy.framing.restrictToOrigin(origin)
|
||||
|
||||
Your app will only render inside frames loaded by `origin`. You can only call
|
||||
this function once with a single origin, and cannot use wildcards or specify
|
||||
multiple origins that are allowed to frame your app. (This is a limitation of
|
||||
the X-Frame-Options header.) Example values of `origin` include
|
||||
`"http://example.com"` and `"https://foo.example.com"`. **This value of the
|
||||
X-Frame-Options header is not yet supported in Chrome or Safari and will be
|
||||
ignored in those browsers. If you need Chrome and/or Safari support, or need to
|
||||
allow multiple domains to frame your application, you can use the frame-ancestors
|
||||
CSP option via the BrowserPolicy.content.allowFrameAncestorsOrigin() function.**
|
||||
|
||||
## BrowserPolicy.framing.allowAll()
|
||||
|
||||
This unsets the X-Frame-Options header, so that your app can be framed by any
|
||||
webpage.
|
||||
|
||||
### Content options
|
||||
|
||||
You can use the functions in this section to control how different types of
|
||||
content can be loaded on your site.
|
||||
|
||||
You can use the following functions to adjust policies on where Javascript and
|
||||
CSS can be run:
|
||||
|
||||
## BrowserPolicy.content.allowInlineScripts()
|
||||
|
||||
Allows inline `<script>` tags, `javascript:` URLs, and inline event handlers.
|
||||
The default policy already allows inline scripts.
|
||||
|
||||
## BrowserPolicy.content.disallowInlineScripts()
|
||||
|
||||
Disallows inline Javascript. Calling this function results in an extra round-trip
|
||||
on page load to retrieve Meteor runtime configuration that is usually part of an
|
||||
inline script tag.
|
||||
|
||||
## BrowserPolicy.content.allowEval()
|
||||
|
||||
Allows the creation of Javascript code from strings using function such as `eval()`.
|
||||
|
||||
## BrowserPolicy.content.disallowEval()
|
||||
|
||||
Disallows eval and related functions. Note: The default policy disallows eval,
|
||||
though for almost all Meteor apps it is enabled by the `dynamic-imports` package
|
||||
|
||||
## BrowserPolicy.content.allowInlineStyles()
|
||||
|
||||
Allows inline style tags and style attributes. The default policy already allows
|
||||
inline styles.
|
||||
|
||||
## BrowserPolicy.content.disallowInlineStyles()
|
||||
|
||||
Disallows inline CSS.
|
||||
|
||||
Finally, you can configure a whitelist of allowed requests that various types of
|
||||
content can make. The following functions are defined for the content types
|
||||
script, object, image, media, font, frame, frame-ancestors, style, and connect.
|
||||
|
||||
## BrowserPolicy.content.allowContentTypeOrigin(origin)
|
||||
|
||||
Allows this type of content to be loaded from the given origin. `origin` is a
|
||||
string and can include an optional scheme (such as `http` or `https`), an
|
||||
optional wildcard at the beginning, and an optional port which can be a
|
||||
wildcard. Examples include `example.com`, `https://*.example.com`, and
|
||||
`example.com:*`. You can call these functions multiple times with different
|
||||
origins to specify a whitelist of allowed origins. Origins that don't specify a
|
||||
protocol will allow content over both HTTP and HTTPS: passing `example.com` will
|
||||
allow content from both `http://example.com` and `https://example.com`.
|
||||
|
||||
## BrowserPolicy.content.allowContentTypeDataUrl()
|
||||
|
||||
Allows this type of content to be loaded from a `data:` URL.
|
||||
|
||||
## BrowserPolicy.content.allowContentTypeSameOrigin()
|
||||
|
||||
Allows this type of content to be loaded from the same origin as your app.
|
||||
|
||||
## BrowserPolicy.content.disallowContentType()
|
||||
|
||||
Disallows this type of content on your app.
|
||||
|
||||
You can also set policies for all these types of content at once, using these
|
||||
functions:
|
||||
|
||||
- `BrowserPolicy.content.allowSameOriginForAll()`,
|
||||
- `BrowserPolicy.content.allowDataUrlForAll()`,
|
||||
- `BrowserPolicy.content.allowOriginForAll(origin)`
|
||||
- `BrowserPolicy.content.disallowAll()`
|
||||
|
||||
For example, if you want to allow the
|
||||
origin `https://foo.com` for all types of content but you want to disable
|
||||
`<object>` tags, you can call
|
||||
`BrowserPolicy.content.allowOriginForAll("https://foo.com")` followed by
|
||||
`BrowserPolicy.content.disallowObject()`.
|
||||
|
||||
Other examples of using the `BrowserPolicy.content` API:
|
||||
|
||||
- `BrowserPolicy.content.disallowFont()` causes the browser to disallow all
|
||||
`<font>` tags.
|
||||
- `BrowserPolicy.content.allowImageOrigin("https://example.com")`
|
||||
allows images to have their `src` attributes point to images served from
|
||||
`https://example.com`.
|
||||
- `BrowserPolicy.content.allowConnectOrigin("https://example.com")` allows XMLHttpRequest
|
||||
and WebSocket connections to `https://example.com`.
|
||||
- `BrowserPolicy.content.allowFrameOrigin("https://example.com")` allows
|
||||
your site to load the origin `https://example.com` in a frame or
|
||||
iframe. The `BrowserPolicy.framing` API allows you to control which
|
||||
sites can frame your site, while
|
||||
`BrowserPolicy.content.allowFrameOrigin` allows you to control which
|
||||
sites can be loaded inside frames on your site.
|
||||
|
||||
Adding `browser-policy-content` to your app also tells certain
|
||||
browsers to avoid sniffing content types away from the declared type
|
||||
(for example, interpreting a text file as JavaScript), using the
|
||||
[X-Content-Type-Options](http://msdn.microsoft.com/en-us/library/ie/gg622941%28v=vs.85%29.aspx)
|
||||
header. To re-enable content sniffing, you can call
|
||||
`BrowserPolicy.content.allowContentTypeSniffing()`.
|
||||
60
v3-docs/docs/packages/bundle-visualizer.md
Normal file
60
v3-docs/docs/packages/bundle-visualizer.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Bundle Visualizer
|
||||
|
||||
The `bundle-visualizer` package is an analysis tool which provides a visual
|
||||
representation within the web browser showing what is included in the initial
|
||||
client bundle. The initial client bundle is the primary package of code
|
||||
downloaded and executed by the browser to run a Meteor application and includes
|
||||
packages which have been added via `meteor add <package>` or Node modules
|
||||
included in the `node_modules` directory and used in an application.
|
||||
|
||||
This visualization can uncover details about which files or packages are
|
||||
occupying space within the initial client bundle. This can be useful in
|
||||
determining which imports might be candidates for being converted to dynamic
|
||||
`import()` statements (which are excluded from the initial client bundle), or
|
||||
for identifying packages which have been inadvertently included in a project.
|
||||
|
||||
## How it works
|
||||
|
||||
This package utilizes the `<hash>.stats.json` files which are written alongside
|
||||
file bundles when the application is ran with the `--production` flag. The
|
||||
specific details for the minified file sizes is added by the minifier package
|
||||
and therefore it's important to review the minifier requirements below.
|
||||
|
||||
## Requirements
|
||||
|
||||
This package requires data provided by the project's minifier. For this reason,
|
||||
it is necessary to use the official `standard-minifier-js` package or a minifier
|
||||
which includes file-size details obtained during minification.
|
||||
|
||||
## Usage
|
||||
|
||||
Since bundle analysis is only truly accurate on a minified bundle and
|
||||
minification does not take place during development (as it is a complex and
|
||||
CPU-intensive process which would substantially slow down normal development)
|
||||
this package must be used in conjunction with the `--production` flag to the
|
||||
`meteor` tool to simulate production bundling and enable minification.
|
||||
|
||||
> **IMPORTANT:** Since this package is active in production mode, it is critical
|
||||
> to only add this package temporarily. This can be easily accomplished using
|
||||
> the `--extra-packages` option to `meteor`.
|
||||
|
||||
### Enabling
|
||||
```sh
|
||||
$ cd app/
|
||||
$ meteor --extra-packages bundle-visualizer --production
|
||||
```
|
||||
|
||||
### Viewing
|
||||
|
||||
Once enabled, view the application in a web-browser as usual
|
||||
(e.g. `http://localhost:3000/`) and the chart will be displayed on top of the
|
||||
application.
|
||||
|
||||
### Disabling
|
||||
|
||||
If you used `--extra-packages`, simply remove `bundle-visualizer` from the list
|
||||
of included packages and run `meteor` as normal.
|
||||
|
||||
> If you've added `bundle-visualizer` permanently with `meteor add`, it is
|
||||
> important to remove this package prior to bundling or deploying to
|
||||
> production with `meteor remove bundle-visualizer`.
|
||||
50
v3-docs/docs/packages/coffeescript.md
Normal file
50
v3-docs/docs/packages/coffeescript.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# CoffeeScript
|
||||
|
||||
[CoffeeScript](http://coffeescript.org/) is a little language that
|
||||
compiles into JavaScript. It provides a simple syntax without lots of
|
||||
braces and parentheses. The code compiles one-to-one into the
|
||||
equivalent JS, and there is no interpretation at runtime.
|
||||
|
||||
CoffeeScript is supported on both the client and the server. Files
|
||||
ending with `.coffee`, `.litcoffee`, or `.coffee.md` are automatically
|
||||
compiled to JavaScript.
|
||||
|
||||
### Namespacing and CoffeeScript
|
||||
|
||||
Here's how CoffeeScript works with Meteor's namespacing.
|
||||
|
||||
* Per the usual CoffeeScript convention, CoffeeScript variables are
|
||||
file-scoped by default (visible only in the `.coffee` file where
|
||||
they are defined.)
|
||||
|
||||
* When writing a package, CoffeeScript-defined variables can be
|
||||
exported like any other variable (see [Package.js](/api/package)). Exporting a variable pulls it up to
|
||||
package scope, meaning that it will be visible to all of the code in
|
||||
your app or package (both `.js` and `.coffee` files).
|
||||
|
||||
* Package-scope variables declared in `.js` files are visible in any
|
||||
`.coffee` files in the same app or project.
|
||||
|
||||
* There is no way to make a package-scope variable from a `.coffee`
|
||||
file other than exporting it. We couldn't figure out a way to make
|
||||
this fit naturally inside the CoffeeScript language. If you want to
|
||||
use package-scope variables with CoffeeScript, one way is to make a
|
||||
short `.js` file that declares all of your package-scope
|
||||
variables. They can then be used and assigned to from `.coffee`
|
||||
files.
|
||||
|
||||
* If you want to share variables between `.coffee` files in the same
|
||||
package, and don't want to separately declare them in a `.js` file,
|
||||
we have an experimental feature that you may like. An object called
|
||||
`share` is visible in CoffeeScript code and is shared across all
|
||||
`.coffee` files in the same package. So, you can write `share.foo`
|
||||
for a value that is shared between all CoffeeScript code in a
|
||||
package, but doesn't escape that package.
|
||||
|
||||
Heavy CoffeeScript users, please let us know how this arrangement
|
||||
works for you, whether `share` is helpful for you, and anything else
|
||||
you'd like to see changed.
|
||||
|
||||
### Modules and CoffeeScript
|
||||
|
||||
See [Modules » Syntax » CoffeeScript](/packages/modules.html#coffeescript).
|
||||
121
v3-docs/docs/packages/dynamic-import.md
Normal file
121
v3-docs/docs/packages/dynamic-import.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Dynamic Imports
|
||||
|
||||
> **Note:** Dynamic imports require Meteor 1.5 or higher.
|
||||
|
||||
The `dynamic-import` package provides an implementation of
|
||||
`Module.prototype.dynamicImport`, an extension of the module runtime which
|
||||
powers the [dynamic `import(...)`](https://github.com/tc39/proposal-dynamic-import)
|
||||
statement, an up-and-coming (ECMA2020) addition to the
|
||||
ECMAScript standard.
|
||||
|
||||
The dynamic `import(...)` statement is a complementary method to the static
|
||||
`import` technique of requiring a module. While a statically `import`-ed
|
||||
module would be bundled into the initial JavaScript bundle, a
|
||||
dynamically `import()`-ed module is fetched from the server at
|
||||
runtime.
|
||||
|
||||
Once a module is fetched dynamically from the server, it is cached permanently
|
||||
on the client and additional requests for the same version of the module will
|
||||
not incur the round-trip request to the server. If the module is changed then a
|
||||
fresh copy will always be retrieved from the server.
|
||||
|
||||
## Usage
|
||||
|
||||
The `import(...)` statement returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises)
|
||||
which is resolved with the `exports` of the module when it has been successfully
|
||||
fetched from the server and is ready to be used.
|
||||
|
||||
Because it's a `Promise`, there are a couple methods developers can use to
|
||||
dictate what will happen upon the availability of the dynamically loaded module:
|
||||
|
||||
### The `.then()` method of the `Promise`
|
||||
|
||||
```js
|
||||
import("tool").then((tool) => tool.task());
|
||||
```
|
||||
|
||||
### By `await`-ing in an asynchronous function
|
||||
|
||||
Meteor supports [`async` and `await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function),
|
||||
which provide a straightforward approach to asynchronously wait for the
|
||||
module to be ready without the need to provide a callback:
|
||||
|
||||
```js
|
||||
async function performTask() {
|
||||
const tool = await import("tool");
|
||||
tool.task();
|
||||
}
|
||||
```
|
||||
|
||||
::: warning
|
||||
**Default exports**
|
||||
|
||||
The `import(...)` `Promise` is resolved with the `exports` of the module.
|
||||
If it's necessary to use the "default" export from a module, it will be
|
||||
available on the `default` property of the resulting object. In the above
|
||||
examples, this means it will be available as `tool.default`. It can be
|
||||
helpful to use parameter de-structuring to provide additional clarity:
|
||||
|
||||
```js
|
||||
import("another-tool").then(({ default: thatTool }) => thatTool.go());
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Using `import()` with dynamic expressions
|
||||
|
||||
If you try to import using any computed expression, such as:
|
||||
|
||||
```js
|
||||
let path = "example";
|
||||
const module = await import(`/libs/${path}.js`);
|
||||
```
|
||||
|
||||
You'll get an error like so:
|
||||
|
||||
```js
|
||||
Error: Cannot find module '/libs/example.js'
|
||||
```
|
||||
|
||||
Meteor’s build process builds a graph of all files that are imported or required
|
||||
using static analysis. It then creates exact bundles of the referenced files
|
||||
and makes them available to the client for `import()`.
|
||||
|
||||
Without a complete import statement (static, dynamic or `require`), Meteor won't
|
||||
make that module available for `import()`.
|
||||
|
||||
The solution to make dynamic expressions work is to create a module "whitelist"
|
||||
that can be read by the build process, but does not actually run. For example:
|
||||
|
||||
```js
|
||||
if (false) {
|
||||
import("/libs/example.js");
|
||||
import("/libs/another-example.js");
|
||||
import("/libs/yet-another-example.js");
|
||||
}
|
||||
```
|
||||
|
||||
Make sure the whitelist is imported from both the client and server entry points.
|
||||
|
||||
## Difference to other bundling systems
|
||||
|
||||
In Meteor's implementation, the client has perfect information about which
|
||||
modules were in the initial bundle, which modules are in the local cache, and
|
||||
which modules still need to be fetched. There is never any overlap between
|
||||
requests made by a single client, nor will there be any unneeded modules in the
|
||||
response from the server. You might call this strategy **exact code splitting**,
|
||||
to differentiate it from bundling.
|
||||
|
||||
Moreover, the initial bundle includes the hashes of all available dynamic
|
||||
modules, so the client doesn't have to ask the server if it can use a cached
|
||||
version of a module, and the same version of the module never needs to be
|
||||
downloaded again by the same client. This caching system has all the benefits of
|
||||
immutable caching.
|
||||
|
||||
Meteor also allows dynamic expressions as long as the dependency is expressed
|
||||
statically somewhere else in your code. This is possible because Meteor's
|
||||
client-side module system understands how to resolve dynamic strings at runtime
|
||||
(which is not true in webpack or browserify, because they replace module
|
||||
identifier strings with numbers). However, the set of available modules is
|
||||
constrained by the string literals that you, the programmer, explicitly decided
|
||||
to allow to be imported (either directly or in a whitelist).
|
||||
309
v3-docs/docs/packages/ecmascript.md
Normal file
309
v3-docs/docs/packages/ecmascript.md
Normal file
@@ -0,0 +1,309 @@
|
||||
# ECMAScript
|
||||
|
||||
This package lets you use new JavaScript language features that are part
|
||||
of the [ECMAScript 2015 specification](http://www.ecma-international.org/ecma-262/6.0/) but are
|
||||
not yet supported by all engines or browsers. Unsupported syntax is
|
||||
automatically translated into standard JavaScript that behaves the same
|
||||
way.
|
||||
|
||||
[This video](https://www.youtube.com/watch?v=05Z6YGiZKmE) from the July
|
||||
2015 Meteor Devshop gives an overview of how the package works, and what
|
||||
it provides.
|
||||
|
||||
## Usage
|
||||
|
||||
The `ecmascript` package registers a compiler plugin that transpiles
|
||||
ECMAScript 2015+ to ECMAScript 5 (standard JS) in all `.js` files. By
|
||||
default, this package is pre-installed for all new apps and packages.
|
||||
|
||||
To add this package to an existing app, run the following command from
|
||||
your app directory:
|
||||
|
||||
```bash
|
||||
meteor add ecmascript
|
||||
```
|
||||
|
||||
To add the `ecmascript` package to an existing package, include the
|
||||
statement `api.use('ecmascript');` in the `Package.onUse` callback in your
|
||||
`package.js` file:
|
||||
|
||||
```js
|
||||
Package.onUse((api) => {
|
||||
api.use('ecmascript');
|
||||
});
|
||||
```
|
||||
|
||||
## Supported ES2015 Features
|
||||
|
||||
### Syntax
|
||||
|
||||
The `ecmascript` package uses [Babel](http://babeljs.io/) to compile
|
||||
ES2015 syntax to ES5 syntax. Many but not all ES2015 features can be
|
||||
simulated by Babel, and `ecmascript` enables most of the features
|
||||
supported by Babel.
|
||||
|
||||
Here is a list of the Babel transformers that are currently enabled:
|
||||
|
||||
* [`es3.propertyLiterals`](https://babeljs.io/docs/advanced/transformers/es3/property-literals/)<br>
|
||||
Makes it safe to use reserved keywords like `catch` as unquoted keys in
|
||||
object literals. For example, `{ catch: 123 }` is translated to `{ "catch": 123 }`.
|
||||
|
||||
* [`es3.memberExpressionLiterals`](https://babeljs.io/docs/advanced/transformers/es3/member-expression-literals/)<br>
|
||||
Makes it safe to use reserved keywords as property names. For
|
||||
example, `object.catch` is translated to `object["catch"]`.
|
||||
|
||||
* [`es6.arrowFunctions`](http://babeljs.io/docs/learn-es2015/#arrows)<br>
|
||||
Provides a shorthand for function expressions. For example,
|
||||
`[1, 2, 3].map(x => x + 1)` evaluates to `[2, 3, 4]`. If `this` is used
|
||||
in the body of the arrow function, it will be automatically bound to the
|
||||
value of `this` in the enclosing scope.
|
||||
|
||||
* [`es6.literals`](http://babeljs.io/docs/learn-es2015/#binary-and-octal-literals)<br>
|
||||
Adds support for binary and octal numeric literals. For example,
|
||||
`0b111110111 === 503` and `0o767 === 503`.
|
||||
|
||||
* [`es6.templateLiterals`](http://babeljs.io/docs/learn-es2015/#template-strings)<br>
|
||||
Enables multi-line strings delimited by backticks instead of quotation
|
||||
marks, with variable interpolation:
|
||||
```js
|
||||
var name = 'Ben';
|
||||
var message = `My name is:
|
||||
${name}`;
|
||||
```
|
||||
|
||||
* [`es6.classes`](http://babeljs.io/docs/learn-es2015/#classes)<br>
|
||||
Enables `class` syntax:
|
||||
```js
|
||||
class Base {
|
||||
constructor(a, b) {
|
||||
this.value = a * b;
|
||||
}
|
||||
}
|
||||
|
||||
class Derived extends Base {
|
||||
constructor(a, b) {
|
||||
super(a + 1, b + 1);
|
||||
}
|
||||
}
|
||||
|
||||
var d = new Derived(2, 3);
|
||||
d.value; // 12
|
||||
```
|
||||
|
||||
* [`es6.constants`](https://babeljs.io/docs/learn-es2015/#let-const)<br>
|
||||
Allows defining block-scoped variables that are not allowed to be
|
||||
redefined:
|
||||
```js
|
||||
const GOLDEN_RATIO = (1 + Math.sqrt(5)) / 2;
|
||||
|
||||
// This reassignment will be forbidden by the compiler:
|
||||
GOLDEN_RATIO = 'new value';
|
||||
```
|
||||
|
||||
* [`es6.blockScoping`](http://babeljs.io/docs/learn-es2015/#let-const)<br>
|
||||
Enables the `let` and `const` keywords as alternatives to `var`. The key
|
||||
difference is that variables defined using `let` or `const` are
|
||||
visible only within the block where they are declared, rather than being
|
||||
visible anywhere in the enclosing function. For example:
|
||||
```js
|
||||
function example(condition) {
|
||||
let x = 0;
|
||||
if (condition) {
|
||||
let x = 1;
|
||||
console.log(x);
|
||||
} else {
|
||||
console.log(x);
|
||||
x = 2;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
example(true); // logs 1, returns 0
|
||||
example(false); // logs 0, returns 2
|
||||
```
|
||||
|
||||
* [`es6.properties.shorthand`](https://babeljs.io/docs/learn-es2015/#enhanced-object-literals)<br>
|
||||
Allows omitting the value of an object literal property when the desired
|
||||
value is held by a variable that has the same name as the property
|
||||
key. For example, instead of writing `{ x: x, y: y, z: "asdf" }` you can
|
||||
just write `{ x, y, z: "asdf" }`. Methods can also be written without
|
||||
the `: function` property syntax:
|
||||
```js
|
||||
var obj = {
|
||||
oldWay: function (a, b) { ... },
|
||||
newWay(a, b) { ... }
|
||||
};
|
||||
```
|
||||
|
||||
* [`es6.properties.computed`](http://babeljs.io/docs/learn-es2015/#enhanced-object-literals)<br>
|
||||
Allows object literal properties with dynamically computed keys:
|
||||
```js
|
||||
var counter = 0;
|
||||
function getKeyName() {
|
||||
return 'key' + counter++;
|
||||
}
|
||||
|
||||
var obj = {
|
||||
[getKeyName()]: 'zero',
|
||||
[getKeyName()]: 'one',
|
||||
};
|
||||
|
||||
obj.key0; // 'zero'
|
||||
obj.key1; // 'one'
|
||||
```
|
||||
|
||||
* [`es6.parameters`](http://babeljs.io/docs/learn-es2015/#default-rest-spread)<br>
|
||||
Default expressions for function parameters, evaluated whenever the parameter
|
||||
is `undefined`, `...rest` parameters for capturing remaining
|
||||
arguments without using the `arguments` object:
|
||||
```js
|
||||
function add(a = 0, ...rest) {
|
||||
rest.forEach(n => a += n);
|
||||
return a;
|
||||
}
|
||||
|
||||
add(); // 0
|
||||
add(1, 2, 3); // 6
|
||||
```
|
||||
|
||||
* [`es6.spread`](http://babeljs.io/docs/learn-es2015/#default-rest-spread)<br>
|
||||
Allows an array of arguments to be interpolated into a list of arguments
|
||||
to a function call, `new` expression, or array literal, without using
|
||||
`Function.prototype.apply`:
|
||||
```js
|
||||
add(1, ...[2, 3, 4], 5); // 15
|
||||
new Node('name', ...children);
|
||||
[1, ...[2, 3, 4], 5]; // [1, 2, 3, 4, 5]
|
||||
```
|
||||
|
||||
* [`es6.forOf`](http://babeljs.io/docs/learn-es2015/#iterators-for-of)<br>
|
||||
Provides an easy way to iterate over the elements of a collection:
|
||||
```js
|
||||
let sum = 0;
|
||||
for (var x of [1, 2, 3]) {
|
||||
sum += x;
|
||||
}
|
||||
sum; // 6
|
||||
```
|
||||
|
||||
* [`es6.destructuring`](http://babeljs.io/docs/learn-es2015/#destructuring)<br>
|
||||
Destructuring is the technique of using an array or object pattern on
|
||||
the left-hand side of an assignment or declaration, in place of the
|
||||
usual variable or parameter, so that certain sub-properties of the value
|
||||
on the right-hand side will be bound to identifiers that appear within the
|
||||
pattern. Perhaps the simplest example is swapping two variables without
|
||||
using a temporary variable:
|
||||
```js
|
||||
[a, b] = [b, a];
|
||||
```
|
||||
Extracting a specific property from an object:
|
||||
```js
|
||||
let { username: name } = user;
|
||||
// is equivalent to
|
||||
let name = user.username;
|
||||
```
|
||||
Instead of taking a single opaque `options` parameter, a function can
|
||||
use an object destructuring pattern to name the expected options:
|
||||
```js
|
||||
function run({ command, args, callback }) { ... }
|
||||
|
||||
run({
|
||||
command: 'git',
|
||||
args: ['status', '.'],
|
||||
callback(error, status) { ... },
|
||||
unused: 'whatever'
|
||||
});
|
||||
```
|
||||
|
||||
* [`es7.objectRestSpread`](https://github.com/sebmarkbage/ecmascript-rest-spread)<br>
|
||||
Supports catch-all `...rest` properties in object literal declarations
|
||||
and assignments:
|
||||
```js
|
||||
let { x, y, ...rest } = { x: 1, y: 2, a: 3, b: 4 };
|
||||
x; // 1
|
||||
y; // 2
|
||||
rest; // { a: 3, b: 4 }
|
||||
```
|
||||
Also enables `...spread` properties in object literal expressions:
|
||||
```js
|
||||
let n = { x, y, ...rest };
|
||||
n; // { x: 1, y: 2, a: 3, b: 4 }
|
||||
```
|
||||
|
||||
* [`es7.trailingFunctionCommas`](https://github.com/jeffmo/es-trailing-function-commas)<br>
|
||||
Allows the final parameter of a function to be followed by a comma,
|
||||
provided that parameter is not a `...rest` parameter.
|
||||
|
||||
* [`flow`](https://babeljs.io/docs/advanced/transformers/other/flow/)<br>
|
||||
Permits the use of [Flow](http://flowtype.org/) type annotations. These
|
||||
annotations are simply stripped from the code, so they have no effect on
|
||||
the code's behavior, but you can run the `flow` tool over your code to
|
||||
check the types if desired.
|
||||
|
||||
### Polyfills
|
||||
|
||||
The ECMAScript 2015 standard library has grown to include new APIs and
|
||||
data structures, some of which can be implemented ("polyfilled") using
|
||||
JavaScript that runs in all engines and browsers today. Here are three new
|
||||
constructors that are guaranteed to be available when the `ecmascript`
|
||||
package is installed:
|
||||
|
||||
* [`Promise`](https://github.com/meteor/promise)<br>
|
||||
A `Promise` allows its owner to wait for a value that might not be
|
||||
available yet. See [this tutorial](https://www.promisejs.org/) for more
|
||||
details about the API and motivation. The Meteor `Promise`
|
||||
implementation is especially useful because it runs all callback
|
||||
functions in recycled `Fiber`s, so you can use any Meteor API, including
|
||||
those that yield (e.g. `HTTP.get`, `Meteor.call`, or `MongoCollection`),
|
||||
and you never have to call `Meteor.bindEnvironment`.
|
||||
|
||||
* [`Map`](https://github.com/zloirock/core-js#map)<br>
|
||||
An associative key-value data structure where the keys can be any
|
||||
JavaScript value (not just strings). Lookup and insertion take constant
|
||||
time.
|
||||
|
||||
* [`Set`](https://github.com/zloirock/core-js#set)<br>
|
||||
A collection of unique JavaScript values of any type. Lookup and
|
||||
insertion take constant time.
|
||||
|
||||
* [`Symbol`](https://github.com/zloirock/core-js#ecmascript-6-symbol)<br>
|
||||
An implementation of the global
|
||||
[`Symbol`](http://www.2ality.com/2014/12/es6-symbols.html)s namespace
|
||||
that enables a number of other ES2015 features, such as `for`-`of` loops
|
||||
and `Symbol.iterator` methods: `[1,2,3][Symbol.iterator]()`.
|
||||
|
||||
* Polyfills for the following [`Object`](https://github.com/zloirock/core-js#ecmascript-6-object)-related methods:
|
||||
* `Object.assign`
|
||||
* `Object.is`
|
||||
* `Object.setPrototypeOf`
|
||||
* `Object.prototype.toString` (fixes `@@toStringTag` support)<br>
|
||||
|
||||
Complete reference [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object).
|
||||
|
||||
* Polyfills for the following [`String`](https://github.com/zloirock/core-js#ecmascript-6-string)-related methods:
|
||||
* `String.fromCodePoint`
|
||||
* `String.raw`
|
||||
* `String.prototype.includes`
|
||||
* `String.prototype.startsWith`
|
||||
* `String.prototype.endsWith`
|
||||
* `String.prototype.repeat`
|
||||
* `String.prototype.codePointAt`
|
||||
* `String.prototype.trim`<br>
|
||||
|
||||
Complete reference [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String).
|
||||
|
||||
* Polyfills for the following [`Array`](https://github.com/zloirock/core-js#ecmascript-6-array)-related methods:
|
||||
* `Array.from`
|
||||
* `Array.of`
|
||||
* `Array.prototype.copyWithin`
|
||||
* `Array.prototype.fill`
|
||||
* `Array.prototype.find`
|
||||
* `Array.prototype.findIndex`
|
||||
|
||||
Complete reference [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array).
|
||||
|
||||
* Polyfills for the following [`Function`](https://github.com/zloirock/core-js#ecmascript-6-function)-related properties:
|
||||
* `Function.prototype.name` (fixes IE9+)
|
||||
* `Function.prototype[Symbol.hasInstance]` (fixes IE9+)
|
||||
|
||||
Complete reference [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array).
|
||||
69
v3-docs/docs/packages/fetch.md
Normal file
69
v3-docs/docs/packages/fetch.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Fetch
|
||||
|
||||
Isomorphic modern/legacy/Node polyfill for WHATWG fetch().
|
||||
|
||||
This package replaces the `http` package for HTTP calls. `fetch` package provides polyfill for the [WHATWG fetch specification](https://fetch.spec.whatwg.org/) for legacy browsers or defaults to the global class which is available in modern browsers and Node. It is recommended that you use this package for compatibility with non-modern browsers.
|
||||
|
||||
For more information we recommend [reading the MDN articles](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) about it as this article covers only basic usage in Meteor.
|
||||
|
||||
## Usage
|
||||
|
||||
### Installation
|
||||
|
||||
To add this package to an existing app, run the following command from
|
||||
your app directory:
|
||||
|
||||
```bash
|
||||
meteor add fetch
|
||||
```
|
||||
|
||||
To add the `fetch` package to an existing package, include the
|
||||
statement `api.use('fetch');` in the `Package.onUse` callback in your
|
||||
`package.js` file:
|
||||
|
||||
```js
|
||||
Package.onUse((api) => {
|
||||
api.use("fetch");
|
||||
});
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
You can import `fetch`, `Headers`, `Request` and `Response` classes from `meteor/fetch`.
|
||||
|
||||
```js
|
||||
import { fetch, Headers, Request, Response } from "meteor/fetch";
|
||||
```
|
||||
|
||||
For the most part though, you will only need to import `fetch` and `Headers`.
|
||||
|
||||
```js
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { fetch, Headers } from 'meteor/fetch';
|
||||
|
||||
async function postData (url, data) {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'POST', // *GET, POST, PUT, DELETE, etc.
|
||||
mode: 'cors', // no-cors, *cors, same-origin
|
||||
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
|
||||
credentials: 'same-origin', // include, *same-origin, omit
|
||||
headers: new Headers({
|
||||
Authorization: 'Bearer my-secret-key',
|
||||
'Content-Type': 'application/json'
|
||||
}),
|
||||
redirect: 'follow', // manual, *follow, error
|
||||
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
|
||||
body: JSON.stringify(data) // body data type must match "Content-Type" header
|
||||
});
|
||||
const data = await response.json();
|
||||
return response(null, data);
|
||||
} catch (err) {
|
||||
return response(err, null);
|
||||
}
|
||||
}
|
||||
|
||||
const postDataCall = Meteor.wrapAsync(postData);
|
||||
const results = postDataCall('https://www.example.org/statsSubmission', { totalUsers: 55 }));
|
||||
|
||||
```
|
||||
133
v3-docs/docs/packages/hot-module-replacement.md
Normal file
133
v3-docs/docs/packages/hot-module-replacement.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Hot Module Replacement
|
||||
|
||||
Hot Module Replacement (HMR) is a method of updating javascript modules within a running application. This reduces the feedback cycle while developing, so you can view and test changes quicker. In Meteor's implementation, the app can be updated before the build has even finished.
|
||||
|
||||
::: tip
|
||||
`hot-module-replacement` package was introduced in Meteor 2.0
|
||||
:::
|
||||
|
||||
To enable HMR for an app, it should use the `hot-module-replacement` package. HMR is currently not supported for packages, but packages can depend on the `hot-module-replacement` package to ensure access to the hot API. When a change can not be accepted with HMR, Meteor uses hot code push to update the app, as is normally done when HMR is not used.
|
||||
|
||||
HMR currently supports the modern web architecture. It is always disabled in other architectures and in production.
|
||||
|
||||
### How the app is updated
|
||||
|
||||
While rebuilding a supported architecture, Meteor checks which files were modified, and sends the modified files to the client. The client then uses this process:
|
||||
|
||||
1. It checks if the modified module accepts or declines updates. If the module does neither, Meteor looks at the modules that imported it to see if they accept or decline the update, and then the modules that import those, and so on. If all paths it followed leads to modules that accept the update, it uses HMR. Otherwise, it uses hot code push.
|
||||
2. Many js modules do things that have a long term effect on the app. They might create Tracker autoruns, register event listeners, or have UI components that have been rendered. Modules can register dispose handlers to clean up the old version of a module so it no longer affects the app. At this point in the update process, those dispose handlers are called.
|
||||
3. Meteor runs the new version of the module, the modules that accepted the update, and all of the modules between them. This ensures the module's new exports are used. Because of this, usually the only modules that accept updates are the ones that have no exports or that have another way of updating the exports used by parent modules. At this point, there are two versions of these modules running, but the old version is no longer in use if it was disposed properly.
|
||||
|
||||
For HMR to work properly, the correct modules have to accept or decline updates, and dispose handlers have to be written. Fortunately, most apps do not need to manually do either. Instead, you can use integrations that automatically detect modules that can accept updates, and how to clean up specific types of modules. For example, the React integration is enabled by default in Meteor apps, and is able to automatically update React components.
|
||||
|
||||
### API
|
||||
|
||||
The hot-module-replacement package provides an API to control how HMR is used. The API is available at `module.hot`. Since the API isn't always available (for example, in production or in architectures not supported by HMR), the code should make sure `module.hot` is defined before using it:
|
||||
|
||||
```js
|
||||
if (module.hot) {
|
||||
module.hot.accept();
|
||||
}
|
||||
```
|
||||
|
||||
In a future Meteor version, using the if statement will allow minifiers to remove this block when minifying for production.
|
||||
|
||||
Packages that use the `module.hot` api should use the `hot-module-reload` package to ensure access to the API.
|
||||
|
||||
<ApiBox name="module.hot#accept" hasCustomExample instanceName="module.hot"/>
|
||||
|
||||
HMR reruns files that import the modified modules so the files use the new exports. In addition to configuring which modules can be updated with HMR, `module.hot.accept()` also controls how many files are re-ran. Meteor re-runs the files that import the modified modules, the files that import those files, and so on, until it reaches the modules that accepted the update. Because of this, usually the only modules that accept updates are ones that have no exports, are have another way of updating the exports used by its parent modules.
|
||||
|
||||
<ApiBox name="module.hot#decline" hasCustomExample instanceName="module.hot"/>
|
||||
|
||||
<ApiBox name="module.hot#dispose" hasCustomExample instanceName="module.hot"/>
|
||||
|
||||
The call back is run when this instance of the module will no longer be used. The main use is making sure the instance of the module no longer affects the app. Here is an example where we stop a Tracker computation:
|
||||
|
||||
```js
|
||||
import { setLocale } from '/imports/utils/locale';
|
||||
|
||||
const computation = Tracker.autorun(() => {
|
||||
const user = Meteor.user();
|
||||
|
||||
if (user && user.locale) {
|
||||
setLocale(user.locale);
|
||||
}
|
||||
});
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.dispose(() => {
|
||||
computation.stop();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
If it did not stop the computation, each time the module is reran for HMR, there would be an additional computation. This can lead to unexpected behavior, especially if we've modified the computation function.
|
||||
|
||||
The callback receives a data object that can be mutated to store information for the new instance of the module. This can be used to preserve class instances, state, or other data. For example, this module will preserve the value of the color variable:
|
||||
|
||||
```js
|
||||
|
||||
let color = 'blue';
|
||||
|
||||
export function getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
export function changeColor(newColor) {
|
||||
color = newColor;
|
||||
}
|
||||
|
||||
if (module.hot) {
|
||||
if (module.hot.data) {
|
||||
color = module.hot.data.color;
|
||||
}
|
||||
|
||||
module.hot.dispose(data => {
|
||||
data.color = color;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
When the module first runs, `module.hot.data` is null, so it leaves `color` set to blue. Eventually the app calls `changeColor` and sets the color to `purple`. If the module is re-ran, the old instance of the module stores the color in `data.color`. The new instance retrieves it from `module.hot.data.color`, and registers a new dispose handler for the next time it is re-run.
|
||||
|
||||
<ApiBox name="module.hot#data" hasCustomExample instanceName="module.hot"/>
|
||||
|
||||
<ApiBox name="module.hot#onRequire" hasCustomExample instanceName="module.hot"/>
|
||||
|
||||
This is used by some HMR integrations to detect files that can be automatically updated with HMR, and handle cleaning up the old module instances and migrating state.
|
||||
For example, React Fast Refresh uses this to find the modules that only export React components, and have those modules accept updates.
|
||||
|
||||
```js
|
||||
if (module.hot) {
|
||||
module.hot.onRequire({
|
||||
// requiredModule is the same object available in the
|
||||
// required module as `module`, including access to `module.hot`
|
||||
// and `module.exports`
|
||||
//
|
||||
// parentId is a string with the path of the module that
|
||||
// imported requiredModule.
|
||||
before(requiredModule, parentId) {
|
||||
// Anything returned here is available to the
|
||||
// after callback as the data parameter.
|
||||
return {
|
||||
importedBy: parentId,
|
||||
previouslyEvaluated: !requiredModule.loaded
|
||||
}
|
||||
},
|
||||
after(requiredModule, data) {
|
||||
if (!data.previouslyEvaluated) {
|
||||
console.log(`Finished evaluating ${requiredModule.id}`);
|
||||
console.log(`It was imported by ${data.importedBy}`);
|
||||
console.log(`Its exports are ${requiredModule.exports}`);
|
||||
}
|
||||
|
||||
// canAcceptUpdates would look at the exports, and maybe the imports
|
||||
// to check if this module can safely be updated with HMR.
|
||||
if (requiredModule.hot && canAcceptUpdates(requiredModule)) {
|
||||
requiredModule.hot.accept();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
12
v3-docs/docs/packages/less.md
Normal file
12
v3-docs/docs/packages/less.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# less
|
||||
|
||||
[LESS](http://lesscss.org/) extends CSS with dynamic behavior such as variables, mixins,
|
||||
operations and functions. It allows for more compact stylesheets and
|
||||
helps reduce code duplication in CSS files.
|
||||
|
||||
With the `less` package installed, `.less` files in your application are
|
||||
automatically compiled to CSS and the results are included in the client CSS
|
||||
bundle.
|
||||
|
||||
> If you want to `@import` a file, give it the extension `.import.less`
|
||||
to prevent Meteor from processing it independently.
|
||||
44
v3-docs/docs/packages/logging.md
Normal file
44
v3-docs/docs/packages/logging.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Logging
|
||||
|
||||
The `logging` package provides a standardised way for you to log and display in console various message from your application.
|
||||
The added benefit is that among other data it will show you the location where the log was fired,
|
||||
this is useful during debugging to quickly locate where the message is coming from.
|
||||
|
||||
Start by installing the package:
|
||||
|
||||
```bash
|
||||
meteor add logging
|
||||
```
|
||||
|
||||
You can then import the utility anywhere in you code like this:
|
||||
```javascript
|
||||
import { Log } from 'meteor/logging'
|
||||
```
|
||||
|
||||
You can then call the logging functions in one of the following ways:
|
||||
```javascript
|
||||
Log('starting up') // or Log.info('starting up')
|
||||
Log.error('error message')
|
||||
Log.warn('warning')
|
||||
Log.debug('this will show only in development')
|
||||
```
|
||||
|
||||
Besides passing in strings, you can also pass in objects. This has few exceptions and special functions associated.
|
||||
First in the root of the object the following keys are not allowed:
|
||||
```javascript
|
||||
'time', 'timeInexact', 'level', 'file', 'line', 'program', 'originApp', 'satellite', 'stderr'
|
||||
```
|
||||
|
||||
On the other hand there is `message` and `app`, which are also reserved, but they will display in more prominent manner:
|
||||
```javascript
|
||||
Log.info({message: 'warning', app: 'DESKTOP', error: { property1: 'foo', property2: 'bar', property3: { foo: 'bar' }} })
|
||||
```
|
||||
will turn into:
|
||||
```shell
|
||||
E20200519-17:57:41.655(9) [DESKTOP] (main.js:36) warning {"error":{"property1":"foo","property2":"bar","property3":{"foo":"bar"}}}
|
||||
```
|
||||
|
||||
The display of each log is color coded. Info is `blue`, warn is `magenta`, debug is `green` and error is in `red`.
|
||||
|
||||
### Log.debug
|
||||
The `Log.debug()` logging is different from the other calls as these messages will not be displayed in production.
|
||||
34
v3-docs/docs/packages/markdown.md
Normal file
34
v3-docs/docs/packages/markdown.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Markdown
|
||||
|
||||
> Note: This package has been deprecated.
|
||||
|
||||
This package lets you use Markdown in your templates.
|
||||
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
meteor add markdown
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
This package is lazy loaded. Is is not added into the initial Bundle.
|
||||
So you need to import it in your template.
|
||||
|
||||
```js
|
||||
// myTemplate.js
|
||||
import 'meteor/markdown';
|
||||
```
|
||||
|
||||
Then you can use the `markdown` helper in your templates:
|
||||
|
||||
```html
|
||||
<!-- myTemplate.html -->
|
||||
{{#markdown}}I am using __markdown__.{{/markdown}}
|
||||
```
|
||||
|
||||
outputs
|
||||
|
||||
```html
|
||||
<p>I am using <strong>markdown</strong>.</p>
|
||||
```
|
||||
376
v3-docs/docs/packages/modules.md
Normal file
376
v3-docs/docs/packages/modules.md
Normal file
@@ -0,0 +1,376 @@
|
||||
# Modules
|
||||
|
||||
This document explains the usage and key features of the module system used by Meteor.
|
||||
|
||||
> Meteor 1.2 introduced support for [many new ECMAScript 2015 features](https://github.com/meteor/meteor/blob/devel/packages/ecmascript/README.md#supported-es2015-features), one of the most notable omissions was [ES2015 `import` and `export` syntax](http://exploringjs.com/es6/ch_modules.html).
|
||||
|
||||
> Meteor 1.3 filled the gap with a fully standards-compliant module system that works on both the client and the server.
|
||||
|
||||
> Meteor 1.7 introduced `meteor.mainModule` and `meteor.testModule` to `package.json` so Meteor doesn't need special folders anymore for js resources. Also doesn't need to eager load js resources.
|
||||
|
||||
By design, `meteor.mainModule` only affect js resources. For non-js resources, there are still some things that can only be done within imports:
|
||||
|
||||
- only stylesheets within imports can be dynamically imported
|
||||
- you can only control the load order of stylesheets by importing them in js if the stylesheets are within imports
|
||||
|
||||
Any non-js resource outside of imports (and some other special folders) are still eagerly loaded.
|
||||
|
||||
> You can read more about these differences in this [comment](https://github.com/meteor/meteor/pull/11381#issuecomment-818816052).
|
||||
|
||||
## Enabling modules
|
||||
|
||||
It is installed by default for all new apps and packages. Nevertheless, the `modules` package is totally optional.
|
||||
|
||||
If you want to add it to existent apps or packages:
|
||||
|
||||
For apps, this is as easy as `meteor add modules`, or (even better) `meteor add ecmascript`, since the `ecmascript` package *implies* the `modules` package.
|
||||
|
||||
For packages, you can enable `modules` by adding `api.use('modules')` to the `Package.onUse` or `Package.onTest` sections of your `package.js` file.
|
||||
|
||||
Now, you might be wondering what good the `modules` package is without the `ecmascript` package, since `ecmascript` enables `import` and `export` syntax. By itself, the `modules` package provides the CommonJS `require` and `exports` primitives that may be familiar if you’ve ever written Node code, and the `ecmascript` package simply compiles `import` and `export` statements to CommonJS. The `require` and `export` primitives also allow Node modules to run within Meteor application code without modification. Furthermore, keeping `modules` separate allows us to use `require` and `exports` in places where using `ecmascript` is tricky, such as the implementation of the `ecmascript` package itself.
|
||||
|
||||
While the `modules` package is useful by itself, we very much encourage using the `ecmascript` package (and thus `import` and `export`) instead of using `require` and `exports` directly. If you need convincing, here's a [presentation](http://benjamn.github.io/empirenode-2015) that explains the differences.
|
||||
|
||||
## Basic syntax
|
||||
|
||||
### ES2015
|
||||
|
||||
Although there are a number of different variations of `import` and `export` syntax, this section describes the essential forms that everyone should know.
|
||||
|
||||
First, you can `export` any named declaration on the same line where it was declared:
|
||||
|
||||
```js
|
||||
// exporter.js
|
||||
export var a = ...;
|
||||
export let b = ...;
|
||||
export const c = ...;
|
||||
export function d() { ... }
|
||||
export function* e() { ... }
|
||||
export class F { ... }
|
||||
```
|
||||
|
||||
These declarations make the variables `a`, `b`, `c` (and so on) available not only within the scope of the `exporter.js` module, but also to other modules that `import` from `exporter.js`.
|
||||
|
||||
If you prefer, you can `export` variables by name, rather than prefixing their declarations with the `export` keyword:
|
||||
|
||||
```js
|
||||
// exporter.js
|
||||
function g() { ... }
|
||||
let h = g();
|
||||
|
||||
// At the end of the file
|
||||
export { g, h };
|
||||
```
|
||||
|
||||
All of these exports are *named*, which means other modules can import them using those names:
|
||||
|
||||
```js
|
||||
// importer.js
|
||||
import { a, c, F, h } from './exporter';
|
||||
new F(a, c).method(h);
|
||||
```
|
||||
|
||||
If you’d rather use different names, you’ll be glad to know `export` and `import` statements can rename their arguments:
|
||||
|
||||
```js
|
||||
// exporter.js
|
||||
export { g as x };
|
||||
g(); // Same as calling `y()` in importer.js
|
||||
```
|
||||
|
||||
```js
|
||||
// importer.js
|
||||
import { x as y } from './exporter';
|
||||
y(); // Same as calling `g()` in exporter.js
|
||||
```
|
||||
|
||||
As with CommonJS `module.exports`, it is possible to define a single *default* export:
|
||||
|
||||
```js
|
||||
// exporter.js
|
||||
export default any.arbitrary(expression);
|
||||
```
|
||||
|
||||
This default export may then be imported without curly braces, using any name the importing module chooses:
|
||||
|
||||
```js
|
||||
// importer.js
|
||||
import Value from './exporter';
|
||||
// Value is identical to the exported expression
|
||||
```
|
||||
|
||||
Unlike CommonJS `module.exports`, the use of default exports does not prevent the simultaneous use of named exports. Here is how you can combine them:
|
||||
|
||||
```js
|
||||
// importer.js
|
||||
import Value, { a, F } from './exporter';
|
||||
```
|
||||
|
||||
In fact, the default export is conceptually just another named export whose name happens to be "default":
|
||||
|
||||
```js
|
||||
// importer.js
|
||||
import { default as Value, a, F } from './exporter';
|
||||
```
|
||||
|
||||
These examples should get you started with `import` and `export` syntax. For further reading, here is a very detailed [explanation](http://www.2ality.com/2014/09/es6-modules-final.html) by [Axel Rauschmayer](https://twitter.com/rauschma) of every variation of `import` and `export` syntax.
|
||||
|
||||
### CommonJS
|
||||
|
||||
You don’t need to use the `ecmascript` package or ES2015 syntax in order to use modules. Just like Node.js in the pre-ES2015 days, you can use `require` and `module.exports`—that’s what the `import` and `export` statements are compiling into, anyway.
|
||||
|
||||
ES2015 `import` lines like these:
|
||||
|
||||
```js
|
||||
import { AccountsTemplates } from 'meteor/useraccounts:core';
|
||||
import '../imports/startup/client/routes.js';
|
||||
```
|
||||
|
||||
can be written with CommonJS like this:
|
||||
|
||||
```js
|
||||
var UserAccountsCore = require('meteor/useraccounts:core');
|
||||
require('../imports/startup/client/routes.js');
|
||||
```
|
||||
|
||||
and you can access `AccountsTemplates` via `UserAccountsCore.AccountsTemplates`.
|
||||
|
||||
Note that files don’t need a `module.exports` if they’re required like `routes.js` is in this example, without assignment to any variable. The code in `routes.js` will simply be included and executed in place of the above `require` statement.
|
||||
|
||||
ES2015 `export` statements like these:
|
||||
|
||||
```js
|
||||
export const insert = new ValidatedMethod({ ... });
|
||||
export default incompleteCountDenormalizer;
|
||||
```
|
||||
|
||||
can be rewritten to use CommonJS `module.exports`:
|
||||
|
||||
```js
|
||||
module.exports.insert = new ValidatedMethod({ ... });
|
||||
module.exports.default = incompleteCountDenormalizer;
|
||||
```
|
||||
|
||||
You can also simply write `exports` instead of `module.exports` if you prefer. If you need to `require` from an ES2015 module with a `default` export, you can access the export with `require('package').default`.
|
||||
|
||||
There is a case where you might *need* to use CommonJS, even if your project has the `ecmascript` package: if you want to conditionally include a module. `import` statements must be at top-level scope, so they cannot be within an `if` block. If you’re writing a common file, loaded on both client and server, you might want to import a module in only one or the other environment:
|
||||
|
||||
```js
|
||||
if (Meteor.isClient) {
|
||||
require('./client-only-file.js');
|
||||
}
|
||||
```
|
||||
|
||||
Note that dynamic calls to `require()` (where the name being required can change at runtime) cannot be analyzed correctly and may result in broken client bundles. This is also discussed in [the guide](http://guide.meteor.com/structure.html#using-require).
|
||||
|
||||
### CoffeeScript
|
||||
|
||||
CoffeeScript has been a first-class supported language since Meteor’s early days. Even though today we recommend ES2015, we still intend to support CoffeeScript fully.
|
||||
|
||||
As of CoffeeScript 1.11.0, [CoffeeScript supports `import` and `export` statements natively](http://coffeescript.org/#modules). Make sure you are using the latest version of the [CoffeeScript package](https://atmospherejs.com/meteor/coffeescript) in your project to get this support. New projects created today will get this version with `meteor add coffeescript`. Make sure you don’t forget to include the `ecmascript` and `modules` packages: `meteor add ecmascript`. (The `modules` package is implied by `ecmascript`.)
|
||||
|
||||
CoffeeScript `import` syntax is nearly identical to the ES2015 syntax you see above:
|
||||
|
||||
```coffee
|
||||
import { Meteor } from 'meteor/meteor'
|
||||
import SimpleSchema from 'simpl-schema'
|
||||
import { Lists } from './lists.coffee'
|
||||
```
|
||||
|
||||
You can also use traditional CommonJS syntax with CoffeeScript.
|
||||
|
||||
## Modular application structure
|
||||
|
||||
Use in your application `package.json` file the section `meteor`.
|
||||
|
||||
> This is available since Meteor 1.7
|
||||
|
||||
```json
|
||||
{
|
||||
"meteor": {
|
||||
"mainModule": {
|
||||
"client": "client/main.js",
|
||||
"server": "server/main.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When specified, these entry points will define in which files Meteor is going to start the evaluation process for each architecture (client and server).
|
||||
|
||||
This way Meteor is not going to eager load any other js files.
|
||||
|
||||
There is also an architecture for the `legacy` client, which is useful if you want to load polyfills or other code for old browsers before importing the main module for the modern client.
|
||||
|
||||
In addition to `meteor.mainModule`, the `meteor` section of `package.json` may also specify `meteor.testModule` to control which test modules are loaded by `meteor test` or `meteor test --full-app`:
|
||||
|
||||
```json
|
||||
{
|
||||
"meteor": {
|
||||
"mainModule": {
|
||||
"client": "client/main.js",
|
||||
"server": "server/main.js"
|
||||
},
|
||||
"testModule": "tests.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If your client and server test files are different, you can expand the testModule configuration using the same syntax as mainModule:
|
||||
|
||||
```json
|
||||
{
|
||||
"meteor": {
|
||||
"mainModule": {
|
||||
"client": "client/main.js",
|
||||
"server": "server/main.js"
|
||||
},
|
||||
"testModule": {
|
||||
"client": "client/tests.js",
|
||||
"server": "server/tests.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The same test module will be loaded whether or not you use the `--full-app` option.
|
||||
|
||||
Any tests that need to detect `--full-app` should check `Meteor.isAppTest`.
|
||||
|
||||
The module(s) specified by `meteor.testModule` can import other test modules at runtime, so you can still distribute test files across your codebase; just make sure you import the ones you want to run.
|
||||
|
||||
To disable eager loading of modules on a given architecture, simply provide a mainModule value of false:
|
||||
|
||||
```json
|
||||
{
|
||||
"meteor": {
|
||||
"mainModule": {
|
||||
"client": false,
|
||||
"server": "server/main.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Historic behind Modular application structure
|
||||
|
||||
If you want to understand how Meteor works without `meteor.mainModule` on `package.json` keep reading this section, but we don't recommend this approach anymore.
|
||||
|
||||
Before the release of Meteor 1.3, the only way to share values between files in an application was to assign them to global variables or communicate through shared variables like `Session` (variables which, while not technically global, sure do feel syntactically identical to global variables). With the introduction of modules, one module can refer precisely to the exports of any other specific module, so global variables are no longer necessary.
|
||||
|
||||
If you are familiar with modules in Node, you might expect modules not to be evaluated until the first time you import them. However, because earlier versions of Meteor evaluated all of your code when the application started, and we care about backwards compatibility, eager evaluation is still the default behavior.
|
||||
|
||||
If you would like a module to be evaluated *lazily* (in other words: on demand, the first time you import it, just like Node does it), then you should put that module in an `imports/` directory (anywhere in your app, not just the root directory), and include that directory when you import the module: `import {stuff} from './imports/lazy'`. Note: files contained by `node_modules/` directories will also be evaluated lazily (more on that below).
|
||||
|
||||
## Modular package structure
|
||||
|
||||
If you are a package author, in addition to putting `api.use('modules')` or `api.use('ecmascript')` in the `Package.onUse` section of your `package.js` file, you can also use a new API called `api.mainModule` to specify the main entry point for your package:
|
||||
|
||||
```js
|
||||
Package.describe({
|
||||
name: 'my-modular-package'
|
||||
});
|
||||
|
||||
Npm.depends({
|
||||
moment: '2.10.6'
|
||||
});
|
||||
|
||||
Package.onUse((api) => {
|
||||
api.use('modules');
|
||||
api.mainModule('server.js', 'server');
|
||||
api.mainModule('client.js', 'client');
|
||||
api.export('Foo');
|
||||
});
|
||||
```
|
||||
|
||||
Now `server.js` and `client.js` can import other files from the package source directory, even if those files have not been added using the `api.addFiles` function.
|
||||
|
||||
When you use `api.mainModule`, the exports of the main module are exposed globally as `Package['my-modular-package']`, along with any symbols exported by `api.export`, and thus become available to any code that imports the package. In other words, the main module gets to decide what value of `Foo` will be exported by `api.export`, as well as providing other properties that can be explicitly imported from the package:
|
||||
|
||||
```js
|
||||
// In an application that uses 'my-modular-package':
|
||||
import { Foo as ExplicitFoo, bar } from 'meteor/my-modular-package';
|
||||
console.log(Foo); // Auto-imported because of `api.export`.
|
||||
console.log(ExplicitFoo); // Explicitly imported, but identical to `Foo`.
|
||||
console.log(bar); // Exported by server.js or client.js, but not auto-imported.
|
||||
```
|
||||
|
||||
Note that the `import` is `from 'meteor/my-modular-package'`, not `from 'my-modular-package'`. Meteor package identifier strings must include the prefix `meteor/...` to disambiguate them from npm packages.
|
||||
|
||||
Finally, since this package is using the new `modules` package, and the package `Npm.depends` on the "moment" npm package, modules within the package can `import moment from 'moment'` on both the client and the server. This is great news, because previous versions of Meteor allowed npm imports only on the server, via `Npm.require`.
|
||||
|
||||
### Lazy loading modules from a package
|
||||
|
||||
Packages can also specify a *lazy* main module:
|
||||
|
||||
```js
|
||||
Package.onUse(function (api) {
|
||||
api.mainModule("client.js", "client", { lazy: true });
|
||||
});
|
||||
```
|
||||
|
||||
This means the `client.js` module will not be evaluated during app
|
||||
startup unless/until another module imports it, and will not even be
|
||||
included in the client bundle if no importing code is found.
|
||||
|
||||
To import a method named `exportedPackageMethod`, simply:
|
||||
|
||||
```js
|
||||
import { exportedPackageMethod } from "meteor/<package name>";
|
||||
```
|
||||
|
||||
> Note: Packages with `lazy` main modules cannot use `api.export` to export global
|
||||
symbols to other packages/apps. Also, prior to Meteor 1.4.4.2 it is necessary to explicitly name the file containing the module: `import "meteor/<package name>/client.js"`.
|
||||
|
||||
|
||||
## Local `node_modules`
|
||||
|
||||
Before Meteor 1.3, the contents of `node_modules` directories in Meteor application code were completely ignored. When you enable `modules`, those useless `node_modules` directories suddenly become infinitely more useful:
|
||||
|
||||
```sh
|
||||
meteor create modular-app
|
||||
cd modular-app
|
||||
mkdir node_modules
|
||||
npm install moment
|
||||
echo "import moment from 'moment';" >> modular-app.js
|
||||
echo 'console.log(moment().calendar());' >> modular-app.js
|
||||
meteor
|
||||
```
|
||||
|
||||
When you run this app, the `moment` library will be imported on both the client and the server, and both consoles will log output similar to: `Today at 7:51 PM`. Our hope is that the possibility of installing Node modules directly within an app will reduce the need for npm wrapper packages such as https://atmospherejs.com/momentjs/moment.
|
||||
|
||||
A version of the `npm` command comes bundled with every Meteor installation, and (as of Meteor 1.3) it's quite easy to use: `meteor npm ...` is synonymous with `npm ...`, so `meteor npm install moment` will work in the example above. (Likewise, if you don't have a version of `node` installed, or you want to be sure you're using the exact same version of `node` that Meteor uses, `meteor node ...` is a convenient shortcut.) That said, you can use any version of `npm` that you happen to have available. Meteor's module system only cares about the files installed by `npm`, not the details of how `npm` installs those files.
|
||||
|
||||
## File load order
|
||||
|
||||
Before Meteor 1.3, the order in which application files were evaluated was dictated by a set of rules described in the [Application Structure - Default file load order](http://guide.meteor.com/structure.html#load-order) section of the Meteor Guide. These rules could become frustrating when one file depended on a variable defined by another file, particularly when the first file was evaluated after the second file.
|
||||
|
||||
Thanks to modules, any load-order dependency you might imagine can be resolved by adding an `import` statement. So if `a.js` loads before `b.js` because of their file names, but `a.js` needs something defined by `b.js`, then `a.js` can simply `import` that value from `b.js`:
|
||||
|
||||
```js
|
||||
// a.js
|
||||
import { bThing } from './b';
|
||||
console.log(bThing, 'in a.js');
|
||||
```
|
||||
|
||||
```js
|
||||
// b.js
|
||||
export var bThing = 'a thing defined in b.js';
|
||||
console.log(bThing, 'in b.js');
|
||||
```
|
||||
|
||||
Sometimes a module doesn’t actually need to import anything from another module, but you still want to be sure the other module gets evaluated first. In such situations, you can use an even simpler `import` syntax:
|
||||
|
||||
```js
|
||||
// c.js
|
||||
import './a';
|
||||
console.log('in c.js');
|
||||
```
|
||||
|
||||
No matter which of these modules is imported first, the order of the `console.log` calls will always be:
|
||||
|
||||
```js
|
||||
console.log(bThing, 'in b.js');
|
||||
console.log(bThing, 'in a.js');
|
||||
console.log('in c.js');
|
||||
```
|
||||
97
v3-docs/docs/packages/oauth-encryption.md
Normal file
97
v3-docs/docs/packages/oauth-encryption.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# OAuth Encryption
|
||||
Encrypts sensitive login secrets stored in the database such as a
|
||||
login service's application secret key and users' access tokens.
|
||||
|
||||
|
||||
## Generating a Key
|
||||
|
||||
The encryption key is 16 bytes, encoded in Base64.
|
||||
|
||||
To generate a key:
|
||||
|
||||
```bash
|
||||
$ meteor node -e 'console.log(require("crypto").randomBytes(16).toString("base64"))'
|
||||
```
|
||||
|
||||
|
||||
## Using oauth-encryption with accounts
|
||||
|
||||
On the server only, use the `oauthSecretKey` option to `Accounts.config`:
|
||||
|
||||
```js
|
||||
Accounts.config({ oauthSecretKey: 'onsqJ+1e4iGFlV0nhZYobg==' });
|
||||
```
|
||||
|
||||
This call to `Accounts.config` should be made at load time (place at
|
||||
the top level of your source file), not called from inside of a
|
||||
`Meteor.startup` block.
|
||||
|
||||
To avoid storing the secret key in your application's source code, you
|
||||
can use [`Meteor.settings`](../api/meteor.md#Meteor-settings):
|
||||
|
||||
```js
|
||||
Accounts.config({ oauthSecretKey: Meteor.settings.oauthSecretKey });
|
||||
```
|
||||
|
||||
|
||||
## Migrating unencrypted user tokens
|
||||
|
||||
This example for Twitter shows how existing unencrypted user tokens
|
||||
can be encrypted. The query finds user documents which have a Twitter
|
||||
access token but not the `algorithm` field which is created when the
|
||||
token is encrypted. The relevant fields in the service data are then
|
||||
encrypted.
|
||||
|
||||
```js
|
||||
const cursor = Meteor.users.find({
|
||||
$and: [
|
||||
{ 'services.twitter.accessToken': { $exists: true } },
|
||||
{ 'services.twitter.accessToken.algorithm': { $exists: false } }
|
||||
]
|
||||
});
|
||||
|
||||
cursor.forEach((userDoc) => {
|
||||
const set = {};
|
||||
|
||||
['accessToken', 'accessTokenSecret', 'refreshToken'].forEach((field) => {
|
||||
const plaintext = userDoc.services.twitter[field];
|
||||
|
||||
if (!_.isString(plaintext)) {
|
||||
return;
|
||||
}
|
||||
|
||||
set[`services.twitter.${field}`] = OAuthEncryption.seal(
|
||||
plaintext,
|
||||
userDoc._id
|
||||
);
|
||||
});
|
||||
|
||||
Meteor.users.update(userDoc._id, { $set: set });
|
||||
});
|
||||
```
|
||||
|
||||
## Using oauth-encryption without accounts
|
||||
|
||||
If you're using the oauth packages directly instead of through the
|
||||
Meteor accounts packages, you can load the OAuth encryption key
|
||||
directly using `OAuthEncryption.loadKey`:
|
||||
|
||||
```js
|
||||
OAuthEncryption.loadKey('onsqJ+1e4iGFlV0nhZYobg==');
|
||||
```
|
||||
|
||||
If you call `retrieveCredential` (such as
|
||||
`Twitter.retrieveCredential`) as part of your process, you'll find
|
||||
when using oauth-encryption that the sensitive service data fields
|
||||
will be encrypted.
|
||||
|
||||
You can decrypt them using `OAuth.openSecrets`:
|
||||
|
||||
```js
|
||||
const credentials = Twitter.retrieveCredential(token);
|
||||
const serviceData = OAuth.openSecrets(credentials.serviceData);
|
||||
```
|
||||
|
||||
## Using oauth-encryption on Windows
|
||||
|
||||
This package depends on [npm-node-aes-gcm](https://github.com/meteor/meteor/tree/devel/packages/non-core/npm-node-aes-gcm), which requires you to have OpenSSL installed on your system to run. To install OpenSSL on Windows, use one of the binaries on [this page](http://slproweb.com/products/Win32OpenSSL.html). Don't forget to install the Visual Studio 2008 redistributables if you don't have them yet.
|
||||
155
v3-docs/docs/packages/packages-listing.md
Normal file
155
v3-docs/docs/packages/packages-listing.md
Normal file
@@ -0,0 +1,155 @@
|
||||
|
||||
|
||||
[//]: # (Do not edit this file by hand.)
|
||||
|
||||
[//]: # (This is a generated file.)
|
||||
|
||||
[//]: # (If you want to change something in this file)
|
||||
|
||||
[//]: # (go to meteor/docs/generators/packages-listing)
|
||||
|
||||
# Core Packages
|
||||
|
||||
|
||||
### [blaze](https://github.com/meteor/blaze) {#blaze}
|
||||
### [react-packages](https://github.com/meteor/react-packages) {#react-packages}
|
||||
### [accounts-2fa](https://github.com/meteor/meteor/tree/devel/packages/accounts-2fa) {#accounts-2fa}
|
||||
### [accounts-base](https://github.com/meteor/meteor/tree/devel/packages/accounts-base) {#accounts-base}
|
||||
### [accounts-facebook](https://github.com/meteor/meteor/tree/devel/packages/accounts-facebook) {#accounts-facebook}
|
||||
### [accounts-github](https://github.com/meteor/meteor/tree/devel/packages/accounts-github) {#accounts-github}
|
||||
### [accounts-google](https://github.com/meteor/meteor/tree/devel/packages/accounts-google) {#accounts-google}
|
||||
### [accounts-meetup](https://github.com/meteor/meteor/tree/devel/packages/accounts-meetup) {#accounts-meetup}
|
||||
### [accounts-meteor-developer](https://github.com/meteor/meteor/tree/devel/packages/accounts-meteor-developer) {#accounts-meteor-developer}
|
||||
### [accounts-oauth](https://github.com/meteor/meteor/tree/devel/packages/accounts-oauth) {#accounts-oauth}
|
||||
### [accounts-password](https://github.com/meteor/meteor/tree/devel/packages/accounts-password) {#accounts-password}
|
||||
### [accounts-passwordless](https://github.com/meteor/meteor/tree/devel/packages/accounts-passwordless) {#accounts-passwordless}
|
||||
### [accounts-twitter](https://github.com/meteor/meteor/tree/devel/packages/accounts-twitter) {#accounts-twitter}
|
||||
### [accounts-ui](https://github.com/meteor/meteor/tree/devel/packages/accounts-ui) {#accounts-ui}
|
||||
### [accounts-ui-unstyled](https://github.com/meteor/meteor/tree/devel/packages/accounts-ui-unstyled) {#accounts-ui-unstyled}
|
||||
### [accounts-weibo](https://github.com/meteor/meteor/tree/devel/packages/accounts-weibo) {#accounts-weibo}
|
||||
### [allow-deny](https://github.com/meteor/meteor/tree/devel/packages/allow-deny) {#allow-deny}
|
||||
### [audit-argument-checks](https://github.com/meteor/meteor/tree/devel/packages/audit-argument-checks) {#audit-argument-checks}
|
||||
### [autopublish](https://github.com/meteor/meteor/tree/devel/packages/autopublish) {#autopublish}
|
||||
### [autoupdate](https://github.com/meteor/meteor/tree/devel/packages/autoupdate) {#autoupdate}
|
||||
### [babel-compiler](https://github.com/meteor/meteor/tree/devel/packages/babel-compiler) {#babel-compiler}
|
||||
### [babel-runtime](https://github.com/meteor/meteor/tree/devel/packages/babel-runtime) {#babel-runtime}
|
||||
### [base64](https://github.com/meteor/meteor/tree/devel/packages/base64) {#base64}
|
||||
### [binary-heap](https://github.com/meteor/meteor/tree/devel/packages/binary-heap) {#binary-heap}
|
||||
### [boilerplate-generator](https://github.com/meteor/meteor/tree/devel/packages/boilerplate-generator) {#boilerplate-generator}
|
||||
### [boilerplate-generator-tests](https://github.com/meteor/meteor/tree/devel/packages/boilerplate-generator-tests) {#boilerplate-generator-tests}
|
||||
### [browser-policy](https://github.com/meteor/meteor/tree/devel/packages/browser-policy) {#browser-policy}
|
||||
### [browser-policy-common](https://github.com/meteor/meteor/tree/devel/packages/browser-policy-common) {#browser-policy-common}
|
||||
### [browser-policy-content](https://github.com/meteor/meteor/tree/devel/packages/browser-policy-content) {#browser-policy-content}
|
||||
### [browser-policy-framing](https://github.com/meteor/meteor/tree/devel/packages/browser-policy-framing) {#browser-policy-framing}
|
||||
### [caching-compiler](https://github.com/meteor/meteor/tree/devel/packages/caching-compiler) {#caching-compiler}
|
||||
### [callback-hook](https://github.com/meteor/meteor/tree/devel/packages/callback-hook) {#callback-hook}
|
||||
### [check](https://github.com/meteor/meteor/tree/devel/packages/check) {#check}
|
||||
### [constraint-solver](https://github.com/meteor/meteor/tree/devel/packages/constraint-solver) {#constraint-solver}
|
||||
### [context](https://github.com/meteor/meteor/tree/devel/packages/context) {#context}
|
||||
### [core-runtime](https://github.com/meteor/meteor/tree/devel/packages/core-runtime) {#core-runtime}
|
||||
### [crosswalk](https://github.com/meteor/meteor/tree/devel/packages/crosswalk) {#crosswalk}
|
||||
### [ddp](https://github.com/meteor/meteor/tree/devel/packages/ddp) {#ddp}
|
||||
### [ddp-client](https://github.com/meteor/meteor/tree/devel/packages/ddp-client) {#ddp-client}
|
||||
### [ddp-common](https://github.com/meteor/meteor/tree/devel/packages/ddp-common) {#ddp-common}
|
||||
### [ddp-rate-limiter](https://github.com/meteor/meteor/tree/devel/packages/ddp-rate-limiter) {#ddp-rate-limiter}
|
||||
### [ddp-server](https://github.com/meteor/meteor/tree/devel/packages/ddp-server) {#ddp-server}
|
||||
### [deprecated](https://github.com/meteor/meteor/tree/devel/packages/deprecated) {#deprecated}
|
||||
### [dev-error-overlay](https://github.com/meteor/meteor/tree/devel/packages/dev-error-overlay) {#dev-error-overlay}
|
||||
### [diff-sequence](https://github.com/meteor/meteor/tree/devel/packages/diff-sequence) {#diff-sequence}
|
||||
### [disable-oplog](https://github.com/meteor/meteor/tree/devel/packages/disable-oplog) {#disable-oplog}
|
||||
### [dynamic-import](https://github.com/meteor/meteor/tree/devel/packages/dynamic-import) {#dynamic-import}
|
||||
### [ecmascript](https://github.com/meteor/meteor/tree/devel/packages/ecmascript) {#ecmascript}
|
||||
### [ecmascript-runtime](https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime) {#ecmascript-runtime}
|
||||
### [ecmascript-runtime-client](https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime-client) {#ecmascript-runtime-client}
|
||||
### [ecmascript-runtime-server](https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime-server) {#ecmascript-runtime-server}
|
||||
### [ejson](https://github.com/meteor/meteor/tree/devel/packages/ejson) {#ejson}
|
||||
### [email](https://github.com/meteor/meteor/tree/devel/packages/email) {#email}
|
||||
### [es5-shim](https://github.com/meteor/meteor/tree/devel/packages/es5-shim) {#es5-shim}
|
||||
### [facebook-config-ui](https://github.com/meteor/meteor/tree/devel/packages/facebook-config-ui) {#facebook-config-ui}
|
||||
### [facebook-oauth](https://github.com/meteor/meteor/tree/devel/packages/facebook-oauth) {#facebook-oauth}
|
||||
### [facts-base](https://github.com/meteor/meteor/tree/devel/packages/facts-base) {#facts-base}
|
||||
### [facts-ui](https://github.com/meteor/meteor/tree/devel/packages/facts-ui) {#facts-ui}
|
||||
### [fetch](https://github.com/meteor/meteor/tree/devel/packages/fetch) {#fetch}
|
||||
### [force-ssl](https://github.com/meteor/meteor/tree/devel/packages/force-ssl) {#force-ssl}
|
||||
### [force-ssl-common](https://github.com/meteor/meteor/tree/devel/packages/force-ssl-common) {#force-ssl-common}
|
||||
### [geojson-utils](https://github.com/meteor/meteor/tree/devel/packages/geojson-utils) {#geojson-utils}
|
||||
### [github-config-ui](https://github.com/meteor/meteor/tree/devel/packages/github-config-ui) {#github-config-ui}
|
||||
### [github-oauth](https://github.com/meteor/meteor/tree/devel/packages/github-oauth) {#github-oauth}
|
||||
### [google-config-ui](https://github.com/meteor/meteor/tree/devel/packages/google-config-ui) {#google-config-ui}
|
||||
### [google-oauth](https://github.com/meteor/meteor/tree/devel/packages/google-oauth) {#google-oauth}
|
||||
### [hot-code-push](https://github.com/meteor/meteor/tree/devel/packages/hot-code-push) {#hot-code-push}
|
||||
### [hot-module-replacement](https://github.com/meteor/meteor/tree/devel/packages/hot-module-replacement) {#hot-module-replacement}
|
||||
### [id-map](https://github.com/meteor/meteor/tree/devel/packages/id-map) {#id-map}
|
||||
### [insecure](https://github.com/meteor/meteor/tree/devel/packages/insecure) {#insecure}
|
||||
### [inter-process-messaging](https://github.com/meteor/meteor/tree/devel/packages/inter-process-messaging) {#inter-process-messaging}
|
||||
### [launch-screen](https://github.com/meteor/meteor/tree/devel/packages/launch-screen) {#launch-screen}
|
||||
### [localstorage](https://github.com/meteor/meteor/tree/devel/packages/localstorage) {#localstorage}
|
||||
### [logging](https://github.com/meteor/meteor/tree/devel/packages/logging) {#logging}
|
||||
### [logic-solver](https://github.com/meteor/meteor/tree/devel/packages/logic-solver) {#logic-solver}
|
||||
### [meetup-config-ui](https://github.com/meteor/meteor/tree/devel/packages/meetup-config-ui) {#meetup-config-ui}
|
||||
### [meetup-oauth](https://github.com/meteor/meteor/tree/devel/packages/meetup-oauth) {#meetup-oauth}
|
||||
### [meteor](https://github.com/meteor/meteor/tree/devel/packages/meteor) {#meteor}
|
||||
### [meteor-base](https://github.com/meteor/meteor/tree/devel/packages/meteor-base) {#meteor-base}
|
||||
### [meteor-developer-config-ui](https://github.com/meteor/meteor/tree/devel/packages/meteor-developer-config-ui) {#meteor-developer-config-ui}
|
||||
### [meteor-developer-oauth](https://github.com/meteor/meteor/tree/devel/packages/meteor-developer-oauth) {#meteor-developer-oauth}
|
||||
### [meteor-platform](https://github.com/meteor/meteor/tree/devel/packages/meteor-platform) {#meteor-platform}
|
||||
### [meteor-tool](https://github.com/meteor/meteor/tree/devel/packages/meteor-tool) {#meteor-tool}
|
||||
### [minifier-css](https://github.com/meteor/meteor/tree/devel/packages/minifier-css) {#minifier-css}
|
||||
### [minifier-js](https://github.com/meteor/meteor/tree/devel/packages/minifier-js) {#minifier-js}
|
||||
### [minimongo](https://github.com/meteor/meteor/tree/devel/packages/minimongo) {#minimongo}
|
||||
### [mobile-experience](https://github.com/meteor/meteor/tree/devel/packages/mobile-experience) {#mobile-experience}
|
||||
### [mobile-status-bar](https://github.com/meteor/meteor/tree/devel/packages/mobile-status-bar) {#mobile-status-bar}
|
||||
### [modern-browsers](https://github.com/meteor/meteor/tree/devel/packages/modern-browsers) {#modern-browsers}
|
||||
### [modules](https://github.com/meteor/meteor/tree/devel/packages/modules) {#modules}
|
||||
### [modules-runtime](https://github.com/meteor/meteor/tree/devel/packages/modules-runtime) {#modules-runtime}
|
||||
### [modules-runtime-hot](https://github.com/meteor/meteor/tree/devel/packages/modules-runtime-hot) {#modules-runtime-hot}
|
||||
### [mongo](https://github.com/meteor/meteor/tree/devel/packages/mongo) {#mongo}
|
||||
### [mongo-dev-server](https://github.com/meteor/meteor/tree/devel/packages/mongo-dev-server) {#mongo-dev-server}
|
||||
### [mongo-id](https://github.com/meteor/meteor/tree/devel/packages/mongo-id) {#mongo-id}
|
||||
### [mongo-livedata](https://github.com/meteor/meteor/tree/devel/packages/mongo-livedata) {#mongo-livedata}
|
||||
### [npm-mongo](https://github.com/meteor/meteor/tree/devel/packages/npm-mongo) {#npm-mongo}
|
||||
### [oauth](https://github.com/meteor/meteor/tree/devel/packages/oauth) {#oauth}
|
||||
### [oauth-encryption](https://github.com/meteor/meteor/tree/devel/packages/oauth-encryption) {#oauth-encryption}
|
||||
### [oauth1](https://github.com/meteor/meteor/tree/devel/packages/oauth1) {#oauth1}
|
||||
### [oauth2](https://github.com/meteor/meteor/tree/devel/packages/oauth2) {#oauth2}
|
||||
### [ordered-dict](https://github.com/meteor/meteor/tree/devel/packages/ordered-dict) {#ordered-dict}
|
||||
### [package-stats-opt-out](https://github.com/meteor/meteor/tree/devel/packages/package-stats-opt-out) {#package-stats-opt-out}
|
||||
### [package-version-parser](https://github.com/meteor/meteor/tree/devel/packages/package-version-parser) {#package-version-parser}
|
||||
### [promise](https://github.com/meteor/meteor/tree/devel/packages/promise) {#promise}
|
||||
### [random](https://github.com/meteor/meteor/tree/devel/packages/random) {#random}
|
||||
### [rate-limit](https://github.com/meteor/meteor/tree/devel/packages/rate-limit) {#rate-limit}
|
||||
### [react-fast-refresh](https://github.com/meteor/meteor/tree/devel/packages/react-fast-refresh) {#react-fast-refresh}
|
||||
### [reactive-dict](https://github.com/meteor/meteor/tree/devel/packages/reactive-dict) {#reactive-dict}
|
||||
### [reactive-var](https://github.com/meteor/meteor/tree/devel/packages/reactive-var) {#reactive-var}
|
||||
### [reload](https://github.com/meteor/meteor/tree/devel/packages/reload) {#reload}
|
||||
### [reload-safetybelt](https://github.com/meteor/meteor/tree/devel/packages/reload-safetybelt) {#reload-safetybelt}
|
||||
### [retry](https://github.com/meteor/meteor/tree/devel/packages/retry) {#retry}
|
||||
### [routepolicy](https://github.com/meteor/meteor/tree/devel/packages/routepolicy) {#routepolicy}
|
||||
### [server-render](https://github.com/meteor/meteor/tree/devel/packages/server-render) {#server-render}
|
||||
### [service-configuration](https://github.com/meteor/meteor/tree/devel/packages/service-configuration) {#service-configuration}
|
||||
### [session](https://github.com/meteor/meteor/tree/devel/packages/session) {#session}
|
||||
### [sha](https://github.com/meteor/meteor/tree/devel/packages/sha) {#sha}
|
||||
### [shell-server](https://github.com/meteor/meteor/tree/devel/packages/shell-server) {#shell-server}
|
||||
### [socket-stream-client](https://github.com/meteor/meteor/tree/devel/packages/socket-stream-client) {#socket-stream-client}
|
||||
### [standard-app-packages](https://github.com/meteor/meteor/tree/devel/packages/standard-app-packages) {#standard-app-packages}
|
||||
### [standard-minifier-css](https://github.com/meteor/meteor/tree/devel/packages/standard-minifier-css) {#standard-minifier-css}
|
||||
### [standard-minifier-js](https://github.com/meteor/meteor/tree/devel/packages/standard-minifier-js) {#standard-minifier-js}
|
||||
### [standard-minifiers](https://github.com/meteor/meteor/tree/devel/packages/standard-minifiers) {#standard-minifiers}
|
||||
### [static-html](https://github.com/meteor/meteor/tree/devel/packages/static-html) {#static-html}
|
||||
### [test-helpers](https://github.com/meteor/meteor/tree/devel/packages/test-helpers) {#test-helpers}
|
||||
### [test-in-browser](https://github.com/meteor/meteor/tree/devel/packages/test-in-browser) {#test-in-browser}
|
||||
### [test-in-console](https://github.com/meteor/meteor/tree/devel/packages/test-in-console) {#test-in-console}
|
||||
### [test-server-tests-in-console-once](https://github.com/meteor/meteor/tree/devel/packages/test-server-tests-in-console-once) {#test-server-tests-in-console-once}
|
||||
### [tinytest](https://github.com/meteor/meteor/tree/devel/packages/tinytest) {#tinytest}
|
||||
### [tinytest-harness](https://github.com/meteor/meteor/tree/devel/packages/tinytest-harness) {#tinytest-harness}
|
||||
### [tracker](https://github.com/meteor/meteor/tree/devel/packages/tracker) {#tracker}
|
||||
### [twitter-config-ui](https://github.com/meteor/meteor/tree/devel/packages/twitter-config-ui) {#twitter-config-ui}
|
||||
### [twitter-oauth](https://github.com/meteor/meteor/tree/devel/packages/twitter-oauth) {#twitter-oauth}
|
||||
### [typescript](https://github.com/meteor/meteor/tree/devel/packages/typescript) {#typescript}
|
||||
### [underscore](https://github.com/meteor/meteor/tree/devel/packages/underscore) {#underscore}
|
||||
### [underscore-tests](https://github.com/meteor/meteor/tree/devel/packages/underscore-tests) {#underscore-tests}
|
||||
### [url](https://github.com/meteor/meteor/tree/devel/packages/url) {#url}
|
||||
### [webapp](https://github.com/meteor/meteor/tree/devel/packages/webapp) {#webapp}
|
||||
### [webapp-hashing](https://github.com/meteor/meteor/tree/devel/packages/webapp-hashing) {#webapp-hashing}
|
||||
### [weibo-config-ui](https://github.com/meteor/meteor/tree/devel/packages/weibo-config-ui) {#weibo-config-ui}
|
||||
### [weibo-oauth](https://github.com/meteor/meteor/tree/devel/packages/weibo-oauth) {#weibo-oauth}
|
||||
15
v3-docs/docs/packages/random.md
Normal file
15
v3-docs/docs/packages/random.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Random
|
||||
|
||||
|
||||
The `random` package provides several functions for generating random
|
||||
numbers. It uses a cryptographically strong pseudorandom number generator when
|
||||
possible, but falls back to a weaker random number generator when
|
||||
cryptographically strong randomness is not available (on older browsers or on
|
||||
servers that don't have enough entropy to seed the cryptographically strong
|
||||
generator).
|
||||
|
||||
<ApiBox name="Random.id" />
|
||||
<ApiBox name="Random.secret" />
|
||||
<ApiBox name="Random.fraction" />
|
||||
<ApiBox name="Random.choice" />
|
||||
<ApiBox name="Random.hexString" />
|
||||
209
v3-docs/docs/packages/server-render.md
Normal file
209
v3-docs/docs/packages/server-render.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# Server Rendering
|
||||
|
||||
This package implements generic support for server-side rendering in
|
||||
Meteor apps, by providing a mechanism for injecting fragments of HTML into
|
||||
the `<head>` and/or `<body>` of the application's initial HTML response.
|
||||
|
||||
## Usage
|
||||
|
||||
This package exports a function named `onPageLoad` which takes a callback
|
||||
function that will be called at page load (on the client) or whenever a
|
||||
new request happens (on the server).
|
||||
|
||||
The callback receives a `sink` object, which is an instance of either
|
||||
`ClientSink` or `ServerSink` depending on the environment. Both types of
|
||||
`sink` have the same methods, though the server version accepts only HTML
|
||||
strings as content, whereas the client version also accepts DOM nodes.
|
||||
|
||||
The current interface of `{Client,Server}Sink` objects is as follows:
|
||||
|
||||
```js
|
||||
class Sink {
|
||||
// Appends content to the <head>.
|
||||
appendToHead(content)
|
||||
|
||||
// Appends content to the <body>.
|
||||
appendToBody(content)
|
||||
|
||||
// Appends content to the identified element.
|
||||
appendToElementById(id, content)
|
||||
|
||||
// Replaces the content of the identified element.
|
||||
renderIntoElementById(id, content)
|
||||
|
||||
// Redirects request to new location.
|
||||
redirect(location, code)
|
||||
|
||||
|
||||
// server only methods
|
||||
|
||||
// sets the status code of the response.
|
||||
setStatusCode(code)
|
||||
|
||||
// sets a header of the response.
|
||||
setHeader(key, value)
|
||||
|
||||
// gets request headers
|
||||
getHeaders()
|
||||
|
||||
// gets request cookies
|
||||
getCookies()
|
||||
}
|
||||
```
|
||||
|
||||
The `sink` object may also expose additional properties depending on the
|
||||
environment. For example, on the server, `sink.request` provides access to
|
||||
the current `request` object, and `sink.arch` identifies the target
|
||||
architecture of the pending HTTP response (e.g. "web.browser").
|
||||
|
||||
Here is a basic example of `onPageLoad` usage on the server:
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [server.js]
|
||||
import from "react";
|
||||
import { renderToString } from "react-dom/server";
|
||||
import { onPageLoad } from "meteor/server-render";
|
||||
|
||||
import App from "/imports/Server.js";
|
||||
|
||||
onPageLoad(sink => {
|
||||
sink.renderIntoElementById("app", renderToString(
|
||||
<App location={sink.request.url} />
|
||||
));
|
||||
});
|
||||
```
|
||||
|
||||
Likewise on the client:
|
||||
|
||||
```js [client.js]
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { onPageLoad } from "meteor/server-render";
|
||||
|
||||
onPageLoad(async (sink) => {
|
||||
const App = (await import("/imports/Client.js")).default;
|
||||
ReactDOM.hydrate(<App />, document.getElementById("app"));
|
||||
});
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Note that the `onPageLoad` callback function is allowed to return a
|
||||
`Promise` if it needs to do any asynchronous work, and thus may be
|
||||
implemented by an `async` function (as in the client case above).
|
||||
|
||||
Note also that the client example does not end up calling any methods of
|
||||
the `sink` object, because `ReactDOM.hydrate` has its own similar API. In
|
||||
fact, you are not even required to use the `onPageLoad` API on the client,
|
||||
if you have your own ideas about how the client should do its rendering.
|
||||
|
||||
Here is a more complicated example of `onPageLoad` usage on the server,
|
||||
involving the [`styled-components`](https://www.styled-components.com/docs/advanced#server-side-rendering) npm package:
|
||||
|
||||
```js
|
||||
import React from "react";
|
||||
import { onPageLoad } from "meteor/server-render";
|
||||
import { renderToString } from "react-dom/server";
|
||||
import { ServerStyleSheet } from "styled-components";
|
||||
import App from "/imports/Server";
|
||||
|
||||
onPageLoad((sink) => {
|
||||
const sheet = new ServerStyleSheet();
|
||||
const html = renderToString(
|
||||
sheet.collectStyles(<App location={sink.request.url} />)
|
||||
);
|
||||
|
||||
sink.renderIntoElementById("app", html);
|
||||
sink.appendToHead(sheet.getStyleTags());
|
||||
});
|
||||
```
|
||||
|
||||
In this example, the callback not only renders the `<App />` element into
|
||||
the element with `id="app"`, but also appends any `<style>` tag(s)
|
||||
generated during rendering to the `<head>` of the response document.
|
||||
|
||||
Although these examples have all involved React, the `onPageLoad` API is
|
||||
designed to be generically useful for any kind of server-side rendering.
|
||||
|
||||
## Streaming HTML
|
||||
|
||||
React 16 introduced [`renderToNodeStream`](https://reactjs.org/docs/react-dom-server.html#rendertonodestream), which enables the reading of rendered HTML in chunks. This reduces the [TTFB](https://en.wikipedia.org/wiki/Time_to_first_byte) (time to first byte).
|
||||
|
||||
Here is a `renderToNodeStream` example using [styled-components](https://www.styled-components.com). Note the use of `sheet.interleaveWithNodeStream` instead of `sink.appendToHead(sheet.getStyleTags());`:
|
||||
|
||||
```js
|
||||
import React from "react";
|
||||
import { onPageLoad } from "meteor/server-render";
|
||||
import { renderToNodeStream } from "react-dom/server";
|
||||
import { ServerStyleSheet } from "styled-components";
|
||||
import App from "/imports/Server";
|
||||
|
||||
onPageLoad((sink) => {
|
||||
const sheet = new ServerStyleSheet();
|
||||
const appJSX = sheet.collectStyles(<App location={sink.request.url} />);
|
||||
const htmlStream = sheet.interleaveWithNodeStream(renderToNodeStream(appJSX));
|
||||
sink.renderIntoElementById("app", htmlStream);
|
||||
});
|
||||
```
|
||||
|
||||
## Getting data from the request
|
||||
|
||||
In some cases you want to customize meta tags or something else in your response based in the requested URL, for example, if your are loading a page with a specific product in your app maybe you want to include an image and a description for [social previews](https://www.contentkingapp.com/academy/open-graph/).
|
||||
|
||||
You can extract information from the request using the `sink` object.
|
||||
|
||||
```js
|
||||
import { onPageLoad } from "meteor/server-render";
|
||||
|
||||
const getBaseUrlFromHeaders = (headers) => {
|
||||
const protocol = headers["x-forwarded-proto"];
|
||||
const { host } = headers;
|
||||
// we need to have '//' to findOneByHost work as expected
|
||||
return `${protocol ? `${protocol}:` : ""}//${host}`;
|
||||
};
|
||||
|
||||
const getContext = (sink) => {
|
||||
// more details about this implementation here
|
||||
// https://github.com/meteor/meteor/issues/9765
|
||||
const { headers, url, browser } = sink.request;
|
||||
// no useful data will be found for galaxybot requests
|
||||
if (browser && browser.name === "galaxybot") {
|
||||
return null;
|
||||
}
|
||||
|
||||
// when we are running inside cordova we don't want to resolve meta tags
|
||||
if (url && url.pathname && url.pathname.includes("cordova/")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const baseUrl = getBaseUrlFromHeaders(headers);
|
||||
const fullUrl = `${baseUrl}${url.pathname || ""}`;
|
||||
|
||||
return { baseUrl, fullUrl };
|
||||
};
|
||||
|
||||
onPageLoad((sink) => {
|
||||
const { baseUrl, fullUrl } = getContext(sink);
|
||||
|
||||
// product URL contains /product on it
|
||||
const urlParseArray = fullUrl.split("/");
|
||||
|
||||
const productPosition = urlParseArray.indexOf("product");
|
||||
const productId =
|
||||
productPosition !== -1 &&
|
||||
urlParseArray[productPosition + 1].replace("?", "");
|
||||
const product = productId && ProductsCollection.findOne(productId);
|
||||
|
||||
const productTitle = product && `Buy now ${product.name}, ${product.price}`;
|
||||
if (productTitle) {
|
||||
sink.appendToHead(`<title>${productTitle}</title>\n`);
|
||||
sink.appendToHead(`<meta property="og:title" content="${productTitle}">\n`);
|
||||
if (product.imageUrl) {
|
||||
sink.appendToHead(
|
||||
`<meta property="og:image" content="${product.imageUrl}">\n`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
65
v3-docs/docs/packages/standard-minifier-css.md
Normal file
65
v3-docs/docs/packages/standard-minifier-css.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Standard Minifier CSS
|
||||
|
||||
This package is the default css minifier in Meteor apps. In addition to minifying the css code in production builds, it also runs any PostCSS plugins configured for the app.
|
||||
|
||||
## Post CSS
|
||||
|
||||
This package can optionally run [PostCSS](https://postcss.org/) plugins on the css files in your app. To enable:
|
||||
|
||||
1. Install npm peer dependencies:
|
||||
|
||||
```sh
|
||||
meteor npm install -D postcss postcss-load-config
|
||||
```
|
||||
|
||||
2. Add PostCSS Config. Create a `postcss.config.js` file and add a config:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {},
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
> The example config enables the `autoprefixer` postcss plugin. You can install the plugin by running `meteor npm install -D autoprefixer`.
|
||||
|
||||
Learn more about [configuring postcss](https://github.com/postcss/postcss-load-config#packagejson) or find a list of [available plugins](https://www.postcss.parts/).
|
||||
|
||||
After making changes to the PostCSS Config, `meteor` must be restarted for it to use the new config.
|
||||
|
||||
### Exclude Meteor Packages
|
||||
|
||||
In addition to the css files in your app, PostCSS will also process the css files added from Meteor packages. In case you do not want these files to be processed, or they are not compatible with your PostCSS config, you can have PostCSS ignore them by using the `excludedMeteorPackages` option:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {},
|
||||
},
|
||||
excludedMeteorPackages: [
|
||||
'github-config-ui',
|
||||
'constellation:console'
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
### Tailwind CSS
|
||||
|
||||
Tailwind CSS is fully supported. Since HMR applies updates to js files earlier than the css is updated, there can be a delay when using a Tailwind CSS class the first time before the styles are applied.
|
||||
|
||||
|
||||
### Debbuging
|
||||
|
||||
_Since Meteor.js 2.11.0 in this [PR](https://github.com/meteor/meteor/pull/12478) we have a debbug mode for the minifier_
|
||||
|
||||
#### How standard-minifier-css becomes verbose
|
||||
|
||||
- Either of the common debugging commandline arguments
|
||||
- `--verbose`
|
||||
- `--debug`
|
||||
- Environment variable
|
||||
- `DEBUG_CSS`
|
||||
|
||||
Side notes:
|
||||
`DEBUG_CSS=false` or `DEBUG_CSS=0` will prevent it from being verbose regardless of `--verbose` or `--debug` commandline arguments, because `DEBUG_CSS` is specific.
|
||||
19
v3-docs/docs/packages/underscore.md
Normal file
19
v3-docs/docs/packages/underscore.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Underscore
|
||||
|
||||
[Underscore](http://underscorejs.org/) is a utility-belt library for
|
||||
JavaScript that provides support for functional programming. It is
|
||||
invaluable for writing clear, concise JavaScript in a functional style.
|
||||
|
||||
The `underscore` package defines the `_` namespace on both the client
|
||||
and the server.
|
||||
|
||||
::: warning
|
||||
We have slightly modified the way Underscore differentiates between
|
||||
objects and arrays in [collection functions](http://underscorejs.org/#each).
|
||||
The original Underscore logic is to treat any object with a numeric `length`
|
||||
property as an array (which helps it work properly on
|
||||
[`NodeList`s](https://developer.mozilla.org/en-US/docs/Web/API/NodeList)).
|
||||
In Meteor's version of Underscore, objects with a numeric `length` property
|
||||
are treated as objects if they have no prototype (specifically, if
|
||||
`x.constructor === Object`.
|
||||
:::
|
||||
62
v3-docs/docs/packages/url.md
Normal file
62
v3-docs/docs/packages/url.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# URL
|
||||
|
||||
`url` package provides polyfill for the [WHATWG url specification](https://url.spec.whatwg.org/) for legacy browsers or defaults to the global class which is available in modern browsers and Node. It is recommended that you use this package for compatibility with non-modern browsers.
|
||||
|
||||
For more information we recommend [reading the MDN articles](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) about it and looking over the [Node API documentation](https://nodejs.org/api/url.html#url_the_whatwg_url_api) for more details as this article covers only basic usage in Meteor.
|
||||
|
||||
## Usage
|
||||
|
||||
### Installation
|
||||
|
||||
To add this package to an existing app, run the following command from
|
||||
your app directory:
|
||||
|
||||
```bash
|
||||
meteor add url
|
||||
```
|
||||
|
||||
To add the `url` package to an existing package, include the
|
||||
statement `api.use('url');` in the `Package.onUse` callback in your
|
||||
`package.js` file:
|
||||
|
||||
```js
|
||||
Package.onUse((api) => {
|
||||
api.use("url");
|
||||
});
|
||||
```
|
||||
|
||||
After installing the package you can then import the `URL` and `URLSearchParams` from the package and use it as described at MDN and Node documentations.
|
||||
|
||||
### URL
|
||||
|
||||
```js
|
||||
import { URL } from "meteor/url";
|
||||
|
||||
const url = new URL("https://www.meteor.com");
|
||||
```
|
||||
|
||||
You can then use `URL` for example in a [fetch](/packages/fetch) call:
|
||||
|
||||
```js
|
||||
import { URL } from 'meteor/url';
|
||||
import { fetch } from 'meteor/fetch';
|
||||
|
||||
const url = new URL('https://www.example.com/api/reportVisit');
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ siteId: 11 })
|
||||
...
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
### URLSearchParams
|
||||
|
||||
```js
|
||||
import { URLSearchParams } from "meteor/url";
|
||||
|
||||
const searchParams = new URLSearchParams({ query: "WHATWG", location: "MDN" });
|
||||
```
|
||||
|
||||
You can then include `URLSearchParams` in the options for `URL` if you build them separately from when creating the `URL` class.
|
||||
171
v3-docs/docs/packages/webapp.md
Normal file
171
v3-docs/docs/packages/webapp.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# WebApp
|
||||
|
||||
The `webapp` package is what lets your Meteor app serve content to a web
|
||||
browser. It is included in the `meteor-base` set of packages that is
|
||||
automatically added when you run `meteor create`. You can easily build a
|
||||
Meteor app without it - for example if you wanted to make a command-line
|
||||
tool that still used the Meteor package system and DDP.
|
||||
|
||||
This package also allows you to add handlers for HTTP requests.
|
||||
This lets other services access your app's data through an HTTP API, allowing
|
||||
it to easily interoperate with tools and frameworks that don't yet support DDP.
|
||||
|
||||
`webapp` exposes the [express](https://github.com/expressjs/express) API for
|
||||
handling requests through `WebApp.handlers`.
|
||||
Here's an example that will let you handle a specific URL:
|
||||
|
||||
```js
|
||||
// Listen to incoming HTTP requests (can only be used on the server).
|
||||
WebApp.handlers.use("/hello", (req, res, next) => {
|
||||
res.writeHead(200);
|
||||
res.end(`Hello world from: ${Meteor.release}`);
|
||||
});
|
||||
```
|
||||
|
||||
<ApiBox name="WebApp.handlers"/>
|
||||
<ApiBox name="expressHandlersCallback(req, res, next)" hasCustomExample/>
|
||||
|
||||
### Serving a Static Landing Page
|
||||
|
||||
One of the really cool things you can do with WebApp is serve static HTML for a landing page where TTFB (time to first byte) is of utmost importance.
|
||||
|
||||
The [Bundle Visualizer](https://docs.meteor.com/packages/bundle-visualizer.html) and [Dynamic Imports](https://docs.meteor.com/packages/dynamic-import.html) are great tools to help you minimize initial page load times. But sometimes you just need to skinny down your initial page load to bare metal.
|
||||
|
||||
The good news is that WebApp makes this is really easy to do.
|
||||
|
||||
Step one is to create a your static HTML file and place it in the _private_ folder at the root of your application.
|
||||
|
||||
Here's a sample _index.html_ you might use to get started:
|
||||
|
||||
::: code-group
|
||||
|
||||
```html [index.html]
|
||||
<head>
|
||||
<title>Fast Landing Page</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no" />
|
||||
<link rel="stylesheet" href="path to your style sheet etc">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- your content -->
|
||||
</body>
|
||||
|
||||
<script>
|
||||
|
||||
// any functions you need to support your landing page
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
```javascript [index.js]
|
||||
/* global WebApp Assets */
|
||||
import crypto from "crypto";
|
||||
import express from "express";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/", function (req, res, next) {
|
||||
const buf = Assets.getText("index.html");
|
||||
|
||||
if (buf.length > 0) {
|
||||
const eTag = crypto.createHash("md5").update(buf).digest("hex");
|
||||
|
||||
if (req.headers["if-none-match"] === eTag) {
|
||||
res.writeHead(304, "Not Modified");
|
||||
return res.end();
|
||||
}
|
||||
|
||||
res.writeHead(200, {
|
||||
ETag: eTag,
|
||||
"Content-Type": "text/html",
|
||||
});
|
||||
|
||||
return res.end(buf);
|
||||
}
|
||||
|
||||
return res.end("<html><body>Index page not found!</body></html>");
|
||||
});
|
||||
|
||||
WebApp.handlers.use(router);
|
||||
|
||||
```
|
||||
|
||||
:::
|
||||
There are a couple things to think about with this approach.
|
||||
|
||||
We're reading the contents of index.html using the [Assets](../api/assets.md) module that makes it really easy to read files out of the _private_ root folder.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
### Dynamic Runtime Configuration
|
||||
|
||||
In some cases it is valuable to be able to control the **meteor_runtime_config** variable that initializes Meteor at runtime.
|
||||
|
||||
#### Example
|
||||
|
||||
There are occasions when a single Meteor server would like to serve multiple cordova applications that each have a unique `ROOT_URL`. But there are 2 problems:
|
||||
|
||||
1. The Meteor server can only be configured to serve a single `ROOT_URL`.
|
||||
2. The `cordova` applications are build time configured with a specific `ROOT_URL`.
|
||||
|
||||
These 2 conditions break `autoupdate` for the cordova applications. `cordova-plugin-meteor-webapp` will fail the update if the `ROOT_URL` from the server does not match the build time configured `ROOT_URL` of the cordova application.
|
||||
|
||||
To remedy this problem `webapp` has a hook for dynamically configuring `__meteor_runtime_config__` on the server.
|
||||
|
||||
#### Dynamic Runtime Configuration Hook
|
||||
|
||||
```js
|
||||
WebApp.addRuntimeConfigHook(
|
||||
({ arch, request, encodedCurrentConfig, updated }) => {
|
||||
// check the request to see if this is a request that requires
|
||||
// modifying the runtime configuration
|
||||
if (request.headers.domain === "calling.domain") {
|
||||
// make changes to the config for this domain
|
||||
// decode the current runtime config string into an object
|
||||
const config = WebApp.decodeRuntimeConfig(current);
|
||||
// make your changes
|
||||
config.newVar = "some value";
|
||||
config.oldVar = "new value";
|
||||
// encode the modified object to the runtime config string
|
||||
// and return it
|
||||
return WebApp.encodeRuntimeConfig(config);
|
||||
}
|
||||
// Not modifying other domains so return undefined
|
||||
return undefined;
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
<ApiBox name="WebApp.addRuntimeConfigHook"/>
|
||||
<ApiBox name="addRuntimeConfigHookCallback(options)" hasCustomExample/>
|
||||
|
||||
Additionally, 2 helper functions are available to decode the runtime config string and encode the runtime config object.
|
||||
|
||||
<ApiBox name="WebApp.decodeRuntimeConfig"/>
|
||||
<ApiBox name="WebApp.encodeRuntimeConfig"/>
|
||||
|
||||
### Updated Runtime Configuration Hook
|
||||
|
||||
```js
|
||||
const autoupdateCache;
|
||||
// Get a notification when the runtime configuration is updated
|
||||
// for each arch
|
||||
WebApp.addUpdatedNotifyHook(({arch, manifest, runtimeConfig}) => {
|
||||
// Example, see if runtimeConfig.autoupdate has changed and if so
|
||||
// do something
|
||||
if(!_.isEqual(autoupdateCache, runtimeConfig.autoupdate)) {
|
||||
autoupdateCache = runtimeConfig.autoupdate;
|
||||
// do something...
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
<ApiBox name="WebApp.addUpdatedNotifyHook"/>
|
||||
<ApiBox name="addUpdatedNotifyHookCallback(options)" hasCustomExample/>
|
||||
<ApiBox name="main"/>
|
||||
BIN
v3-docs/docs/public/logo.png
Normal file
BIN
v3-docs/docs/public/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user