├── README.md
├── TODO.md
├── meta
└── specification.md
├── modules
├── discussions.md
└── specification.md
├── packages
├── TODO.md
├── notes.md
└── specification.md
├── promises
├── TODO.md
└── specification.md
├── template
└── TODO.md
└── tests
├── specification.md
├── test.js
└── tutorial.md
/README.md:
--------------------------------------------------------------------------------
1 | UncommonJS
2 | ==========
3 |
4 | Decentralized JavaScript conventions and API design. An
5 | opinionated fork of CommonJS. Fork me to show support, fix,
6 | and extend.
7 |
8 |
9 | Contributing
10 | ============
11 |
12 | Specifications are developed in separate directories and
13 | eponymous branches. These are merged one-way into a master
14 | branch to provide the whole picture. I encourage
15 | participants to create their own master branch that only
16 | merges the specifications that they are tracking and
17 | approve.
18 |
19 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 |
2 | To Do
3 | =====
4 |
5 | 1. tests (the unit test specification)
6 | 1. promises
7 | 1. QQ
8 | 1. add promises to modules-async
9 | 1. bytes, buffers, blobs
10 | 1. packages
11 | 1. registry/catalog
12 | 1. fs
13 | 1. io
14 | 1. http
15 | 1. JSGI
16 |
17 |
18 | Done
19 | ====
20 |
21 | 1. modules
22 | 1. define
23 | 1. async
24 | 1. specifications (the meta-specification specification)
25 | 1. promises
26 | 1. thenables
27 | 1. promiseSendables
28 | 1. Q
29 | 1. packages
30 | 1. ``package.json``
31 | 1. mappings
32 | 1. dependencies
33 |
34 |
--------------------------------------------------------------------------------
/meta/specification.md:
--------------------------------------------------------------------------------
1 |
2 | 1. Specifications must be in Markdown format.
3 | 1. Specifications must be physically wrapped at 72 columns.
4 | 1. Specifications must use visually equivalent spaces instead of tabs.
5 | 1. Specifications must align tab stops every 4 spaces.
6 | 1. Specifications must use an outline of numbered sentences so that
7 | they can be easily referenced.
8 | 1. Within Markdown format, all numbered points must be number 1
9 | such that they are easy to edit, diff, and patch.
10 | 1. The markdown ``1.`` must start on and be followed by spaces
11 | until the next tab stop.
12 | 1. Non-normative text (*statements that cannot be tested*) must be
13 | marked down with emphasis or in non-numbered paragraphs following
14 | the specification.
15 | 1. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
16 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in
17 | this document are to be interpreted as described in RFC 2119.
18 |
19 | In Vim, use ``:set tw=72`` or ``textwidth=72`` to configure automatic
20 | physical wrapping at the 72nd column. Select a region and type `gq` to
21 | reflow a paragraph or bullet point. Use ``ts=4`` and ``sw=4``
22 | (``tabstop=4`` and ``shiftwidth=4``) to align text at tab stops.
23 |
24 |
--------------------------------------------------------------------------------
/modules/discussions.md:
--------------------------------------------------------------------------------
1 |
2 | * [http://groups.google.com/group/commonjs/browse_thread/thread/6ad5c2c3b005cb3b/5a0f17e43d347673](RFC require.paths behaviour)
3 | * [http://groups.google.com/group/commonjs/browse_thread/thread/c3682135d72b1f8](Module meta data Proposal) (resurrection of this discussion)
4 | * [http://groups.google.com/group/commonjs/browse_thread/thread/6e6eeb9b3d2990f1](require.main === undefined Options)
5 | * [http://groups.google.com/group/commonjs/browse_thread/thread/0f9e8fc585211f6a](Modules 1.1 Comments)
6 | * [http://groups.google.com/group/commonjs/browse_thread/thread/2f6c87b65e30fb71](Modules/1.1.1 Show of Hands)
7 |
8 |
--------------------------------------------------------------------------------
/modules/specification.md:
--------------------------------------------------------------------------------
1 | This specification establishes a convention for creating a style of
2 | reusable JavaScript modules and systems that will load and link those
3 | modules.
4 |
5 | * Modules are singletons.
6 | * Modules have an implicitly isolated lexical scope.
7 | * Loading may be eager in asynchronous loaders.
8 | * Execution must be lazy.
9 | * With some care, modules may have cyclic dependencies.
10 | * Systems of modules may be isolated but still share the same context.
11 | * Modules may be used both in browsers and servers.
12 |
13 |
14 | Guarantees Made by Module Writers
15 | =================================
16 |
17 | No requirement in this section implies that a module system must enforce
18 | that requirement. These are merely requirements that a module must
19 | satisfy in order to function in any module system.
20 |
21 | 1. A module must be encoded in UTF-8.
22 | 1. A module must only assume that its scope contains ``require``,
23 | ``exports``, and ``module`` in addition to the globals provided by
24 | JavaScript.
25 |
26 | 1. *Depending on any other values limits portability to engines
27 | that do not provide those values and will cause reference errors
28 | on those systems.*
29 |
30 | 1. A module must consider that every object in its scope may be
31 | immutable.
32 |
33 | 1. *Depending on the mutability of an object precludes the use of
34 | the module in a system that shares its global scope among
35 | mutually suspcious programs, like between mashups in a secured
36 | JavaScript context.*
37 |
38 | 1. *It may be acceptable to upgrade an object in scope (shim) if it
39 | does not provide the needed, specified behavior, but this will
40 | limit portability to secure contexts with legacy
41 | implementations.*
42 |
43 | 1. A module must only depend on the specified behavior of values in
44 | scope.
45 |
46 | 1. *Depending on any extension to the specified behavior limits
47 | portability.*
48 |
49 | 1. All calls to ``require`` must be given a single string literal as
50 | the argument, or the value of ``require.main`` if it is defined.
51 |
52 | 1. *If the argument to ``require`` is not a string, the dependency
53 | cannot be discovered and asynchronously loaded before executing
54 | the module. This would limit portability to systems that read
55 | the text of a module to discover dependencies and load them
56 | asynchronously before execution, which is necessary for
57 | portability to browsers and other systems that only support
58 | asynchronous loading.*
59 |
60 | 1. *``require.main`` is guaranteed to already have been loaded, so
61 | it is a reasonable exception to the rule that all dependencies
62 | must be discoverable without executing the module.*
63 |
64 | 1. All calls to ``require`` must be by the name ``require``.
65 |
66 | 1. *If ``require`` takes on a different name, the dependency will
67 | not be discoverable without executing the module.*
68 |
69 | 1. All calls to ``require`` must be given a module identifier as the
70 | argument.
71 |
72 | 1. *Some module loaders may accept values that are not module
73 | identifiers. Depending on this behavior inside a module limits
74 | the portability of the module, particularly to systems that use
75 | packages to isolate the module identifier name space for a set
76 | of modules.*
77 |
78 |
79 | Guarantees Made by Module Interpreters
80 | ======================================
81 |
82 | 1. The top scope of a module must not be shared with any other module.
83 |
84 | 1. *All ``var`` declarations in a module will only be accessible
85 | within that module and will not collide with declarations in
86 | other modules*
87 |
88 | 1. A ``require`` function must exist in a function's lexical scope.
89 |
90 | 1. The ``require`` function must accept a module identifier as its
91 | first and only argument.
92 |
93 | 1. ``require`` must return the same value as ``module.exports`` in
94 | the identified module, or must throw an exception while trying.
95 |
96 | 1. ``require`` may have a ``main`` property.
97 |
98 | 1. The ``require.main`` property may be read-only and
99 | non-configurable.
100 |
101 | 1. The ``require.main`` property must be the same value as
102 | ``module`` in the lexical scope of the first module that
103 | began executing in the current system of modules.
104 |
105 | 1. The ``require.main`` property must be the same value in
106 | every module.
107 |
108 | 1. ``require`` must have a ``resolve`` function.
109 |
110 | 1. ``resolve`` accepts a module identifier as its first and only
111 | argument
112 |
113 | 1. ``resolve`` must return the resolved module identifier
114 | corresponding to the given module identifier relative to
115 | this module's resolved module identifier.
116 |
117 | 1. ``require`` must have an ``async`` function.
118 |
119 | 1. ``async`` must accept an identifier or an array of
120 | identifiers as its first argument.
121 |
122 | 1. A single identifier is equivalent to an array with a
123 | single identifier.
124 |
125 | 1. ``async`` accepts an optional ``callback`` function as its
126 | second argument.
127 |
128 | 1. ``async`` accepts an optional ``errback`` function as its
129 | third argument.
130 |
131 | 1. ``async`` must call ``require`` for each module identifier
132 | in order
133 |
134 | 1. ``async`` may wait for each module's transitively required
135 | modules to asynchronously load before calling ``require``.
136 |
137 | 1. If any ``require`` call throws an exception, or if any
138 | module cannot be loaded before it is required, ``errback``
139 | must be called with the ``Error`` as its argument.
140 |
141 | 1. If every ``require`` call returns, ``async`` must call
142 | ``callback`` with the respective exports for each module
143 | identifier as its arguments.
144 |
145 | 1. An ``exports`` object must exist in a function's lexical scope.
146 |
147 | 1. the ``exports`` object must initially be the same value as
148 | ``module.exports``.
149 |
150 | 1. A ``module`` object must exist in a function's lexical scope.
151 |
152 | 1. The ``module`` object must have an ``id`` property.
153 |
154 | 1. The ``module.id`` property must be a module identifier such
155 | that ``require(module.id)`` must return the
156 | ``module.exports`` value when called in this or any module
157 | in the same system of modules.
158 |
159 | 1. The ``module.id`` property may be read-only and
160 | non-configurable.
161 |
162 | 1. The ``module`` object must have an ``exports`` property.
163 |
164 | 1. The ``module.exports`` property must initially be an empty
165 | object.
166 |
167 | 1. The ``module.exports`` property must be writable and
168 | configurable.
169 |
170 | 1. The ``module`` object may have a ``location`` absolute URL.
171 |
172 | 1. The ``module`` object may have a ``directory`` absolute URL.
173 |
174 | 1. The ``directory`` must be the directory containing the
175 | ``location``.
176 |
177 |
178 | Module Identifiers
179 | ==================
180 |
181 | 1. A module identifier is a string of "terms" delimited by forward
182 | slashes.
183 |
184 | 1. A term is either:
185 |
186 | 1. any combination of lower-case letters, numbers, and hyphens,
187 |
188 | 1. a single dot, "``.``", or
189 |
190 | 1. a double dot, "``..``".
191 |
192 | 1. Module identifiers should not have file-name extensions like
193 | ``.js``.
194 |
195 | 1. Module identifiers may be "relative" or "resolved". A module
196 | identifier is "relative" if the first term is ``.`` or ``..``.
197 |
198 | 1. Top-level identifiers are resolved relative to ``""``.
199 |
200 | 1. The ``require`` function in each module resolves relative
201 | identifiers from the corresponding ``module.id``.
202 |
203 | 1. To resolve any path of module identifiers,
204 |
205 | 1. An array of terms must be initialized to an empty array.
206 |
207 | 1. For each module identifier in the path of identifiers,
208 |
209 | 1. Pop off the last term in the array, provided one exists.
210 |
211 | 1. For each term in a module identifier in order,
212 |
213 | 1. Take no action if the term is ``"."``.
214 |
215 | 1. Pop a term off the end of the array if the term is
216 | ``".."``.
217 |
218 | 1. Push the term on the end of the array otherwise.
219 |
220 | 1. The array of terms must be joined with forward slashes, ``"/"``
221 | to construct the resulting "resolved" identifier.
222 |
223 |
224 | Unspecified
225 | ===========
226 |
227 | This specification leaves the following important points of
228 | interoperability unspecified:
229 |
230 | 1. Whether modules are stored with a database, file system, or factory
231 | functions, or are interchangeable with link libraries.
232 |
233 | 1. Whether ``require`` accepts values that are not module identifiers
234 | as specified here.
235 |
236 |
--------------------------------------------------------------------------------
/packages/TODO.md:
--------------------------------------------------------------------------------
1 |
2 | To Do
3 | =====
4 |
5 | * Registry and catalog specifications
6 |
7 |
--------------------------------------------------------------------------------
/packages/notes.md:
--------------------------------------------------------------------------------
1 |
2 | This specification attempts to assimilate most of the points on
3 | CommmonJS/Packages/1.1.
4 |
5 | Node's module loader gives priority to its own built-ins over the
6 | contents and configuration of packages. This specification clarifies
7 | that the package's name space should come first so that packages do not
8 | unexpectedly break when engines change their built-ins and packages are
9 | not subject to name-space collisions with engines unless they really
10 | need to load those modules.
11 |
12 | This specification encourages engines like Node to add a system for
13 | capability based package linkage, particularly for its own API's, so
14 | that packages are encouraged to explicitly request API's from the
15 | engines they support. The intent is to make it possible for a package
16 | registry to automatically construct a reliable compatibility table which
17 | can be enforced by engines.
18 |
19 | Some things that are not used in practice have been removed.
20 |
21 | Some requirements have been relaxed, with the intent that individual
22 | package managers will enforce stronger requirements based on their own
23 | design. Particularly, some package managers will require "name" and
24 | "version" to be provided, while others will only need "location".
25 |
26 | Consistent hashing has been defined.
27 |
28 | Support for "mappings" has been specified and a translation function for
29 | NPM's "dependencies" has been provided and mandated. It's my hope that
30 | both properties will be supported, even by NPM eventually, since they
31 | both have merits.
32 |
33 | This specification hopes to encourage NPM to eventually relax the name
34 | "node_modules" to "packages", and provides a migration path.
35 |
36 | Requirements necessary for browser based loaders, particularly
37 | deterministic package and module locations, have been addressed.
38 |
39 |
--------------------------------------------------------------------------------
/packages/specification.md:
--------------------------------------------------------------------------------
1 |
2 | 1. A package is a directory.
3 | 1. A package must contain a ``package.json`` file.
4 | 1. ``package.json`` must be valid JSON.
5 | 1. Particular package registries or catalogs may impose a more strict
6 | requirements.
7 | 1. Any properties of ``package.json`` not specified here must occur
8 | under an "overlays" mapping.
9 |
10 |
11 | Informative Configuration
12 | =========================
13 |
14 | 1. In ``package.json``, the following apply to the properties of the
15 | root object.
16 | 1. "name" may be the name of the package.
17 | 1. The name must be a combination of one or more
18 | hyphen-delimited lower-case words and numbers.
19 | 1. "title" may be the title for the package for human readers, if
20 | the package has one.
21 | 1. "version" may be a string representing a semantic version
22 | .
23 | 1. "description" may be a one-line description of the package.
24 | 1. "keywords" is an array of short words that may be used to search
25 | and find this package in a registry.
26 | 1. "author" may be a single person.
27 | 1. "maintainers" may be an array of people.
28 | 1. "contributors" may be an array of people.
29 | 1. "licenses" may be an array of licenses. **This property is not
30 | legally binding and does not necessarily mean your package is
31 | licensed under the terms you define in this property**
32 | 1. The existence of multiple lincenses implies that a user may
33 | chose to be bound by any one of them, not that every user
34 | must be bound by all.
35 | 1. "bugs" may be a string or an object.
36 | 1. A "bugs" string must be the URL for a site where bugs can be
37 | reported.
38 | 1. A "bugs" object may have an "email" property with an email
39 | address to which bugs can be reported.
40 | 1. A "bugs" object may have a "url" property with the URL of a
41 | site where bugs can be reported.
42 | 1. "repositories" may be an array of repositories.
43 | 1. A person can be represented as either a string or an object.
44 | *Note: this implies that the string must only contain information
45 | representable with an equivalent object.*
46 | 1. A person string must begin with a name.
47 | 1. A person string may then contain one email address in angle
48 | brackets (``<>``) delimited by a space.
49 | 1. A person string then may contain one web address in parentheses
50 | (``()``) delimited by a space.
51 | 1. A person object must contain a "name" property.
52 | 1. A person object may contain an "email" property with an email
53 | address.
54 | 1. A person object may contain a "web" property with a URL.
55 | 1. A license may be represented either as a URL or an object.
56 | 1. A license object may have a "type" property, having a value from
57 | any of the parenthesized open source license abbreviations from
58 | .
59 | 1. A license object may have a "url" property
60 | 1. A repository must be represented as an object with "type" and
61 | "url" properties.
62 | 1. A repository "type" may be "git", "svn", "hg", or others
63 | based on their command name.
64 | 1. A repository may specify a "url" property.
65 | 1. A repository may spefify a "path" property if the root of
66 | the project is not the the root of the package.
67 |
68 | Normative Configuration
69 | =======================
70 |
71 | 1. In ``package.json``, the following apply to the properties of the
72 | root object.
73 | 1. "main" may be provided and must be a URL relative to
74 | ``package.json`` to a JavaScript file, by its full name
75 | including its extension.
76 | 1. "overlays" may be provided and must be an object that maps overlay
77 | names to objects conforming to the same specification as the
78 | ``package.json``'s root object.
79 | 1. "directories" may be provided and must be an object that maps
80 | conventional directory names to configured directory names within
81 | the package.
82 | 1. "lib" may be configured
83 | 1. "packages" may be configured, and is a URL relative to the
84 | directory containing ``package.json`` where named package
85 | dependencies may be found.
86 | 1. "mappings" may be provided and must be an object that maps module
87 | identifiers to dependencies.
88 | 1. "registry" may be provided and must be a URL for a package registry.
89 | *What exists at that URL is beyond the scope of this specification*
90 | 1. "hash" may be provided and must be the consitent hash of the
91 | package.
92 | 1. "seed" may be provided and must be a string that is used to seed the
93 | consistent hash of the package.
94 | 1. "manifest" may be provided and must be a recursive directory listing
95 | of a package's contents in lexicographic order with paths
96 | represented as URL's relative to ``package.json``
97 | 1. A package dependency may be represented by a string.
98 | 1. If the string contains "@", it is equivalent to an object with
99 | "name", "version", and "registry" properties.
100 | 1. ``@version`` where the name is implied by the mapping
101 | identifier and the registry is implied by the default
102 | registry location.
103 | 1. ``name@version`` where the registry is implied by the
104 | default registry location.
105 | 1. ``name@version@registry``
106 | 1. Otherwise, the dependency is equivalent to an object with a
107 | "location" property with the dependency as its value.
108 | 1. A package dependency may be represented by an object.
109 | 1. A dependency may have a "location" property. *In the absence of
110 | a location property, a module loader and a package manager may
111 | agree on a location computed based on the "packages" directory
112 | and the "name", "version", and "hash" of the package.*
113 | 1. A dependency may have a "name" property.
114 | 1. With a "name", a dependency may have a "version" property.
115 | 1. With a "name", a dependency may have a "registry" property.
116 | 1. A dependency may have a "capability" property. The capability
117 | must be a URL for a specification of that capability, or an
118 | array of URLs for specifications for that capability.
119 | 1. Each specification must be sufficient alone to satisfy the
120 | dependency of the package. *An array is supported in the
121 | case where there are multiple URLs of specifications that
122 | are effectively equivalent for the purpose of the package,
123 | when supporting package systems do not agree on which URL is
124 | the canonical version.*
125 | 1. A dependency may have a "zip" property that is a URL for a ZIP
126 | file containing the package.
127 | 1. A dependency may have a "tar" property that is a URL for a TAR
128 | or gzipped TAR file containing the package.
129 | 1. The "zip" and "tar" URLs are for the use of package managers and
130 | must not be used in place of "location" or "name" dependencies
131 | by module loaders, unless the module loader also serves as a
132 | package manager. For compatibility with all module loaders, a
133 | "location" for a package must be provided or computable from
134 | some subset of "name", "version", "hash" and the
135 | ``directories.packages`` configuration.
136 | 1. A dependency may have an "overlays" property.
137 | 1. For each applicable overlay name, the mapped object must be
138 | copied over the dependency.
139 | 1. A dependency may have a "hash" property, corresponding to the
140 | consistent hash of the dependency package.
141 | 1. Overlay names are strings that identify a specific implementation of
142 | this specification or a class of implementations such that
143 | configuration specific to those implementations may be copied over
144 | the object containing the overlays.
145 |
146 |
147 | Linkage
148 | =======
149 |
150 | Each package has its own resolved module identifier name-space. In the
151 | parlance of the Modules specification, this means that each package is
152 | an independent system of modules, linked by explicit configuration to
153 | other packages.
154 |
155 | 1. There must not be cyclic dependencies among packages.
156 | 1. If a package has a "name" and the first term of a module identifier
157 | is that name, that module identifier is equivalent to the rest of
158 | the identifier, even if the rest of the identifier is ``""`` (empty
159 | string).
160 | 1. The resolved module identifier ``""`` (empty string) corresponds to
161 | the main module if it exists.
162 | 1. For each mapping, from longest to shortest, if the first terms of
163 | the mapping match the first terms of a module identifier
164 | 1. If there are no subsequent terms in the module identifier, the
165 | dependency must link the ``""`` (empty string) module from the
166 | dependency package.
167 | 1. If there are subsequent terms in the module identifier, the
168 | dependency must link the module from that package with the
169 | remaining terms.
170 | 1. If no mapping corresponds to a module identifier, the package must
171 | link to a module in its own library.
172 | 1. The library of a package when used in a browser consists of the
173 | contents of the ``lib`` directory of the package, where each module
174 | identifier corresponds to the lower-case and extensionless URL
175 | relative to the library directory for all files that have a ``.js``
176 | extension.
177 | 1. ``lib`` may be configured to an alternate location with the
178 | ``directories`` property of ``package.json``.
179 | 1. The library of a package when used in any engine other than the
180 | browser may incorporate the contents of the ``overlays/OVERLAY/lib``
181 | directory before the ``lib`` directory, if it exists.
182 | 1. ``lib`` and ``overlays`` may be configured to alternate
183 | locations with the ``directories`` property of ``package.json``.
184 | 1. In each of these directories, any engine other than the browser
185 | may incoroporate modules with alternate extensions and loaders,
186 | searching for each extension before moving to the next
187 | directory.
188 | 1. If a package requires a capability, a package management system can
189 | loosely link packages that provide a capability and packages that
190 | consume that capability, such that different packages can provide
191 | the same capability on different engines. In particular, it would
192 | make sense for Node's own API's to be loaded as an explicitly
193 | requested capability.
194 |
195 | A variety of arrangements are possible between package managers and
196 | package-aware module loaders.
197 |
198 | 1. The module loader may itself be the package manager, in which case
199 | it is repsonsible for downloading, executing, and linking the
200 | packages. In such cases, the "name", "version", "registry", or
201 | "location" properties of dependencies might be used.
202 | 1. The module loader and package manager may agree on locations where
203 | packages may be stored and found. The module loader may elect to
204 | use the "location" of a package and compute missing locations based
205 | on "packages", "name", "version", and "hash", or some combination
206 | thereof. The package manager would agree to drop packages in those
207 | locations. Alternately, the package manager and module loader might
208 | agree on a database or file system location scheme based on some of
209 | those variables.
210 |
211 |
212 | NPM
213 | ===
214 |
215 | To maintain a semblance of compatibility with NPM 1.
216 |
217 | 1. "dependencies" may be provided and must be an object that maps a
218 | module identifier and a package with the same name to a version of
219 | that package from the NPM registry. These (name/id, version) pairs
220 | may be translated to mappings with the ``"http://npmjs.org"``
221 | registry implied.
222 | 1. "directories" may configure the location of "packages". If a
223 | "dependencies" property exists, the default "packages" location is
224 | "node_modules"
225 |
226 |
227 | Hash
228 | ====
229 |
230 | 1. The consistent hash for a package is a SHA256 hash in lower-case
231 | hexadecimal format.
232 | 1. To compute the package's consistent hash
233 | 1. Digest the "seed" property of ``package.json`` if it exists.
234 | 1. Digest the "main" property of ``package.json`` if it exists.
235 | 1. For each mapping in lexicographic order based on the module
236 | identifier, digest the module identifier, location, name,
237 | version, registry, and hash, in that order, for each property
238 | that is defined.
239 | 1. For each file in a recursive traversal of the package contents
240 | in lexicographic order, except ``package.json``, digest the
241 | binary content.
242 |
243 |
244 | Secure Loading
245 | ==============
246 |
247 | For a package to be loaded with a secure loader:
248 |
249 | 1. A package must provide a "hash".
250 | 1. A package must provide a "manifest".
251 |
252 | A secure loader:
253 |
254 | 1. Must verify the consistent hash of the package before executing any
255 | code that links against it.
256 | 1. Must only acknowledge the existence of paths in the manifest.
257 | 1. Must refuse to load a package that lacks a "hash" or "manifest".
258 |
259 |
--------------------------------------------------------------------------------
/promises/TODO.md:
--------------------------------------------------------------------------------
1 |
2 | To Do
3 | =====
4 |
5 | * Extended Promise Manager API
6 | * Progress Observers and Notifiers
7 | * Address inelegance of sending messages to functions, or
8 | automatically marking functions as unserializable
9 | * Address awkwardness of the ``def`` name.
10 |
11 | Done
12 | ====
13 |
14 | * Sendable Promises
15 | * Promise Manager API
16 |
17 |
--------------------------------------------------------------------------------
/promises/specification.md:
--------------------------------------------------------------------------------
1 | An asynchronous promise loosely represents the eventual result of a
2 | function. A promise is initially "unresolved" and may eventually become
3 | "resolved". A resolution can either be "fulfilled" with a value or
4 | "rejected" with a reason, corresponding by analogy to synchronously
5 | returned values and thrown exceptions respectively. Once a promise has
6 | become resolved, it cannot be resolved again, so a promise's fulfillment
7 | value or rejection reason are guaranteed to be the same across multiple
8 | observations. Although the identity of the value or reason cannot
9 | change, their properties may be mutable.
10 |
11 |
12 | Thenable Promises
13 | =================
14 |
15 | Thenable promises are sufficent for modeling fulfillment and rejection
16 | of promises within a single program. If promises are needed to model
17 | remote objects between cooperating event loops (like workers, processes,
18 | and network clients, servers, and peers), they can either be extended
19 | with the message passing "sendable promises" system, or assimilated by a
20 | sendable promise manager system. If a thenable promise is provided by a
21 | system that is suspicious for safety or security reasons, it is
22 | **necessary** to use a promise manager to provide the normative
23 | guarantees of this specification and the stricter guarantees provided by
24 | the promise manager specification.
25 |
26 | 1. A promise is an object (*that may be a function*) with a ``then``
27 | function.
28 |
29 | 1. When using promises, the existence of a ``then`` function must
30 | be sufficient to distinguish a promise from any other value.
31 |
32 | 1. ``then`` accepts a ``fulfilled`` callback and a ``rejected``
33 | callback.
34 |
35 | 1. ``then`` may be called any number of times to register multiple
36 | observers.
37 |
38 | 1. The callbacks registered by any ``then`` call must not be called
39 | after callbacks registered in a later ``then`` call.
40 |
41 | 1. Callbacks should not be called before ``then`` returns.
42 |
43 | 1. ``fulfilled`` and ``rejected`` are both optional arguments.
44 |
45 | 1. If truthy, ``fulfilled`` must accept a value as its only
46 | argument.
47 |
48 | 1. The ``fulfilled`` callback must be called after the promise
49 | is fulfilled.
50 |
51 | 1. The ``fulfilled`` callback must not be called more than
52 | once.
53 |
54 | 1. The ``fulfilled`` callback must not be called if the
55 | ``rejected`` callback has been called.
56 |
57 | 1. If truthy, ``rejected`` must accept a rejection reason as its
58 | only argument.
59 |
60 | 1. The ``rejected`` callback must be called after the promise
61 | has been rejected.
62 |
63 | 1. The ``rejected`` callback must not be called more than once.
64 |
65 | 1. The ``rejected`` callback must not be called if the
66 | ``fulfilled`` callback has been called.
67 |
68 | 1. ``then`` must return a thenable promise.
69 |
70 | 1. The returned promise must be fulfilled with the value
71 | returned by either callback.
72 |
73 | 1. The returned promise must be rejected if either callback
74 | throws an exception.
75 |
76 | 1. The reason for the rejection must be the error thrown by
77 | the callback.
78 |
79 | Thenable Promise Manager
80 | ========================
81 |
82 | 1. A promise manager is an object with the following functions for
83 | creating and manipulating promises.
84 |
85 | 1. ``defer(annotation)``
86 |
87 | 1. Annotation is an optional string describing the operation
88 | being deferred, which may be used by debuggers to assist in
89 | the visualization of when promises are made and resolved.
90 |
91 | 1. Returns an object with ``promise``, ``resolve``, and
92 | ``reject`` properties.
93 |
94 | 1. ``promise`` is a promise (the "deferred promise").
95 |
96 | 1. ``resolve`` is a function that accepts a promise or a value.
97 |
98 | 1. Ignores all resolutions after the first resolution.
99 |
100 | 1. If the value is a promise, resolves the deferred promise
101 | with the resolved promise, such that any observations of
102 | the resolved promise are forwarded to the deferred
103 | promise.
104 |
105 | 1. If the value is not a promise, fulfills the deferred
106 | promise with the resolved value.
107 |
108 | 1. ``reject`` is a function that accepts a rejection reason
109 |
110 | 1. The rejection reason may be any value.
111 |
112 | 1. Resolves the deferred promise with a rejection promise
113 | for the given reason.
114 |
115 | 1. All functions of the deferred object may be called without
116 | being bound to the deferred object.
117 |
118 | 1. ``when(value, fulfilled, rejected)``
119 |
120 | 1. ``value`` may be a promise or any other value.
121 |
122 | 1. Non-promise values are equivalent to fulfilled promises
123 | with that value.
124 |
125 | 1. Arranges for ``fulfilled`` to be called
126 |
127 | 1. if ``fulfilled`` is truthy
128 |
129 | 1. with the fulfilled value as its argument
130 |
131 | 1. not before ``when`` returns
132 |
133 | 1. if and when the value is fulfilled
134 |
135 | 1. at most once
136 |
137 | 1. if ``rejected`` has not been called
138 |
139 | 1. Arranges for ``rejected`` to be called
140 |
141 | 1. if ``rejected`` is truthy
142 |
143 | 1. with the reason for rejection as its argument
144 |
145 | 1. not before ``when`` returns
146 |
147 | 1. if and when the promised value is rejected
148 |
149 | 1. at most once
150 |
151 | 1. if ``fulfilled`` has not been called
152 |
153 | 1. Must return a promise
154 |
155 | 1. that must be resolved if and when ``fulfilled`` or
156 | ``rejected`` return a value or promise, with that value
157 | or promise as the resolution.
158 |
159 | 1. that must be rejected if and when ``fulfilled`` or
160 | ``rejected`` throw an error, with the error as the reason
161 | for rejection.
162 |
163 | 1. ``resolve(value)``
164 |
165 | 1. ``value`` may be a promise or any other value.
166 |
167 | 1. If ``value`` is a promise, returns that promise.
168 |
169 | 1. If ``value`` is not a promise, returns a promise that
170 | has already been fulfilled with the value.
171 |
172 | 1. ``reject(reason)``
173 |
174 | 1. Returns a promise that has been rejected for the given
175 | reason.
176 |
177 | 1. The reason may be any value.
178 |
179 | 1. ``isPromise(value)``
180 |
181 | 1. Returns a boolean value of whether the given value is a
182 | promise.
183 |
184 | 1. ``isResolved(value)``
185 |
186 | 1. Returns a boolean value of whether the given value is a
187 | resolved promise.
188 |
189 | 1. ``value`` may be any value.
190 |
191 | 1. ``isFulfilled(value)``
192 |
193 | 1. Returns a boolean value of whether the given value is a
194 | resolved and fulfilled promise.
195 |
196 | 1. ``value`` may be any value.
197 |
198 | 1. ``isRejected(value)``
199 |
200 | 1. Returns a boolean value of whether the given value is a
201 | resolved and rejected promise.
202 |
203 | 1. ``value`` may be any value.
204 |
205 |
206 | Sendable Promises
207 | =================
208 |
209 | If a promise is a remote object or a proxy for a remote object, in
210 | addition to being able to observe fulfillment and rejection, it is
211 | useful to be able to pipeline "messages" to such promises so that the
212 | remote promise can rapidly dispatch responses to those messages. It is
213 | also useful to have promises for remote objects and proxies for remote
214 | objects that are not serializable, such as stateful functions or
215 | functions that provide access to capabilities.
216 |
217 | 1. A promise is an object (*that may be a function*) with a
218 | ``promiseSend`` function.
219 |
220 | 1. When using promises, the existence of a ``promiseSend`` function
221 | must be sufficient to distinguish a promise from any other
222 | value.
223 |
224 | 1. The ``promiseSend`` function must accept an "operator name" as
225 | its first argument.
226 |
227 | 1. Operator names are an extensible set of strings. The following
228 | operators are reserved:
229 |
230 | 1. ``"when"``, in which case the third argument must be a
231 | rejection callback.
232 |
233 | 1. A rejection callback must accept a rejection reason (any
234 | value) as its argument.
235 |
236 | 1. ``"get"`` in which case the third argument is a property
237 | name (string).
238 |
239 | 1. ``"put"`` in which case the third argument is a property
240 | name (string) and the fourth is a value for the new
241 | property.
242 |
243 | 1. ``"del"`` in which case the third argument is a property
244 | name.
245 |
246 | 1. ``"post"`` in which case the third argument is a property
247 | name and all subsequent arguments are variadic.
248 |
249 | 1. The ``promiseSend`` function must accept a resolver function as
250 | its second argument.
251 |
252 | 1. The resolver function may eventually be called with a value
253 | or a promise as its argument.
254 |
255 | 1. The ``promiseSend`` function may receive variadic arguments.
256 |
257 | 1. The ``promiseSend`` function must return ``undefined``.
258 |
259 | 1. A sendable promise may also be a thenable promise.
260 |
261 | Any thenable promise can be trivially wrapped with a sendable promise
262 | that implements all of the object-oriented operators by waiting for the
263 | promise to be fulfilled and then performing the corresponding operation.
264 | So, it is only necessary for a promise to implement promiseSend if it is
265 | on the boundary between two processes.
266 |
267 |
268 | Sendable Promise Manager
269 | ========================
270 |
271 | A sendable promise manager extends the thenable promise manager system
272 | with functions that can forward messages to a promise, returning
273 | promises for the fulfilled object's response. It also provides tools
274 | for sending and handling messages, and for annotating an object that is
275 | not serializable.
276 |
277 | 1. A sendable promise manager creates and manipulates both sendable
278 | promises and thenable promises.
279 |
280 | 1. A sendable promise manager must support the thenable promise manager
281 | specification.
282 |
283 | 1. A sendable promise manager must support the following additional
284 | functions.
285 |
286 | 1. ``get``, ``put``, ``del``, ``post``, ``invoke`` are functions.
287 |
288 | 1. These functions must accept an object as their first
289 | argument.
290 |
291 | 1. The object may be a promise or any other value.
292 |
293 | 1. If object is not a promise, it is treated as a fulfilled
294 | promise with its value.
295 |
296 | 1. These functions must return a promise.
297 |
298 | 1. The promise must be rejected with the same reason if the
299 | object promise is rejected.
300 |
301 | 1. The promise must be rejected if the object promise is
302 | fulfilled with a type that does not support properties.
303 |
304 | 1. ``get(object, name)``
305 |
306 | 1. Must fulfill the returned promise with the named property of
307 | of the object if and when it is fulfilled.
308 |
309 | 1. ``put(object, name, value)``
310 |
311 | 1. Must fulfill the returned promise with ``undefined`` if and
312 | when the object is fulfilled and the named property has been
313 | assigned the value.
314 |
315 | 1. Must reject the returned promise if the object is fulfilled
316 | and setting the named property to value throws an exception.
317 |
318 | 1. ``del(object, name)``
319 |
320 | 1. Must fulfill the returned promise with ``undefined`` if and
321 | when the object is fulfilled and an attempt has been made to
322 | delete the named property .
323 |
324 | 1. Must reject the returned promise with an exception if the
325 | object is fulfilled and attempting to delete the named
326 | property throws an exception.
327 |
328 | 1. ``post(object, name, args)``
329 |
330 | 1. Must apply the ``args`` to the named function of object if
331 | and when it is fullfilled.
332 |
333 | 1. Must resolve the returned promise with the return value
334 | of that application.
335 |
336 | 1. Must reject the returned promise if that application
337 | throws an error, using the error as the reason for
338 | rejection.
339 |
340 | 1. ``invoke(object, name, ...args)``
341 |
342 | 1. Must apply the spread, variadic ``args`` to the named
343 | function of object if and when it is fullfilled.
344 |
345 | 1. Must resolve the returned promise with the return value
346 | of that application.
347 |
348 | 1. Must reject the returned promise if that application
349 | throws an error, using the error as the reason for
350 | rejection.
351 |
352 | 1. ``keys(object)``
353 |
354 | 1. Must fulfill the returned promise with an array of the owned
355 | property names of the object if and when it is fulfilled.
356 |
357 | 1. ``makePromise(handlers, fallback)``
358 |
359 | 1. Returns a sendable promise
360 |
361 | 1. Accepts a ``handlers`` object that maps message names to
362 | handler functions that return values or promises,
363 | particularly:
364 |
365 | 1. ``when(rejected)``
366 |
367 | 1. ``get(name)``
368 |
369 | 1. ``put(name)``
370 |
371 | 1. ``del(name)``
372 |
373 | 1. ``post(name, args)``
374 |
375 | 1. Accepts a ``fallback(operator, resolve, ...args)`` function
376 |
377 | 1. ``promiseSend(operator, resolve, ...args)`` calls the
378 | handler with the given operator name from handlers if one
379 | is truthy.
380 |
381 | 1. Calls the handler with the spreaded, variadic arguments
382 | only.
383 |
384 | 1. Calls resolve with the value returned by the handler.
385 |
386 | 1. ``promiseSend(operator, resolve, ...args)`` call the
387 | fallback function if property of the handlers mapping for
388 | the given operator name is falsy.
389 |
390 | 1. Calls fallback with the operator and the spreaded,
391 | variadic arguments only.
392 |
393 | 1. Calls resolve with the value returned by the fallback
394 | function.
395 |
396 | 1. ``send(operator, ...args)``
397 |
398 | 1. Constructs a deferred.
399 |
400 | 1. Calls ``promiseSend(operator, resolve, ...args)``
401 |
402 | 1. Where ``resolve`` is the resolve function of the
403 | deferred.
404 |
405 | 1. Not before ``send`` returns.
406 |
407 | 1. Returns the deferred promise.
408 |
409 | 1. ``resolve(value)``
410 |
411 | 1. Must support ``ref`` as defined by the thenable promise
412 | manager specification.
413 |
414 | 1. Must handle the following messages (per ``makePromise``):
415 |
416 | 1. ``when(errback)``, ignores the errback and returns
417 | ``value``.
418 |
419 | 1. ``get(name)``, returns the named property of ``value``.
420 |
421 | 1. ``put(name, value)``, sets the named property to the
422 | given value of the ref value and returns ``undefined``.
423 |
424 | 1. ``del(name)``, deletes the named property of the
425 | resolved value and returns ``undefined``.
426 |
427 | 1. ``post(name, args)``, calls the named function of the
428 | value and returns the returned value.
429 |
430 | 1. Must reject all other messages with the reason ``"Promise
431 | does not handle OPERATOR"`` where ``OPERATOR`` is the
432 | message operator.
433 |
434 | 1. ``reject(reason)``
435 |
436 | 1. Must support ``reject`` as defined by the thenable promise
437 | manager specification.
438 |
439 | 1. Handles the ``"when"`` operator:
440 |
441 | 1. If the ``rejected`` callback is truthy, calls the
442 | ``rejected`` callback with ``reason`` and returns the
443 | value that the callback returns.
444 |
445 | 1. Otherwise, returns a rejected promise with ``reason``.
446 |
447 | 1. Handles all other messages by forwarding its own reason for
448 | rejection.
449 |
450 | 1. ``defer(annotation)``
451 |
452 | 1. Must support ``defer`` as defined by the thenable promise
453 | manager specification.
454 |
455 | 1. Must eventually forward all messages received to the ``ref``
456 | promise representing the resolution of its ``promise``.
457 |
458 |
--------------------------------------------------------------------------------
/template/TODO.md:
--------------------------------------------------------------------------------
1 |
2 | To Do
3 | =====
4 |
5 | git checkout start-here
6 | git checkout -b $SPEC
7 | cp -r template $SPEC
8 | cd $SPEC
9 |
10 |
11 | 1. README.md (synopsis, index, philosophy, prior art, references,
12 | discussions, implementations)
13 | 1. specification.md (adhering to ../specification/specification.md)
14 | 1. tests/all.js (adhering to ../tests/specification.md)
15 | 1. notes.md (for implementors)
16 | 1. tutorial.md (for users)
17 |
18 |
19 | Done
20 | ====
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tests/specification.md:
--------------------------------------------------------------------------------
1 |
2 | Assertions
3 | ==========
4 |
5 | An assertion object must conform to the following specification.
6 | *Note: the ``"assert"`` module identifier is conventionally reserved
7 | for an assertion module.*
8 |
9 | 1. The assertion object must have an ``AssertionError``
10 | constructor function.
11 |
12 | 1. All instances of ``AssertionError`` must be instances of
13 | ``Error``.
14 |
15 | 1. An ``AssertionError`` must be instantiated with an options
16 | object describing the failure.
17 |
18 | 1. Assertion options must have a ``message`` string.
19 |
20 | 1. Assertion options must have an ``actual`` value.
21 |
22 | 1. Assertion options must have an ``expected`` value, or
23 | string describing the expected value or range of values.
24 |
25 | 1. Assertion options may have an ``operator`` string, that
26 | may represent any JavaScript operator or function name
27 | used to test whether the actual value met the expectation.
28 |
29 | 1. In every assertion function, given its criterion for passing or
30 | failing,
31 |
32 | 1. If ``this`` has a ``pass`` function property and the criterion
33 | is met, the assertion function must call ``pass`` with the
34 | given message.
35 |
36 | 1. If ``this`` has a ``fail`` function property and the criterion
37 | is not met, the assertion function must call ``fail`` with an
38 | ``AssertionError`` object with the corresponding ``message``,
39 | ``actual`` value, ``expected`` value description, and
40 | ``operator`` if applicable.
41 |
42 | 1. If ``this`` does not have a ``fail`` function property and the
43 | criteron is not met, the assertion function must throw an
44 | ``AssertionError`` object with the corresponding ``message``,
45 | ``actual`` value, ``expected`` value description, and
46 | ``operator`` if applicable.
47 |
48 | 1. The assertion object must have a function
49 | ``ok(guard, message_opt)``.
50 |
51 | 1. The criterion of ``ok`` is ``!!guard``.
52 |
53 | 1. The corresponding ``operator`` for an ``AssertionError`` is
54 | ``"=="``.
55 |
56 | 1. The corresponding ``expected`` value for an ``AssertionError``
57 | is ``true``.
58 |
59 | 1. The assertion object must have a function
60 | ``equal(actual, expected, message_opt)``
61 |
62 | 1. The criterion of ``equal`` is that ``actual == expected``.
63 |
64 | 1. The corresponding ``operator`` for an ``AssertionError`` is
65 | ``"=="``.
66 |
67 | 1. The assertion object must have a function
68 | ``notEqual(actual, unexpected, message_opt)``
69 |
70 | 1. The criterion of ``notEqual`` is that ``actual != expected``
71 |
72 | 1. The corresponding ``operator`` for an ``AssertionError`` is
73 | ``"!="``.
74 |
75 | 1. The assertion object must have a function
76 | ``deepEqual(actual, expected, message_opt)``
77 |
78 | 1. The corresponding ``operator`` for an ``AssertionError`` is
79 | ``"deepEqual"``.
80 |
81 | 1. The criterion for ``deepEqual`` is that:
82 |
83 | 1. If ``expected == actual``, they are deeply equal.
84 |
85 | 1. If the expected value is a ``Date``, they are deeply equal
86 | only if the actual value is a ``Date`` representing the
87 | same time.
88 |
89 | 1. If the ``typeof`` ``actual`` or ``expected`` is not
90 | ``"object"``, they are deeply equal if ``expected ==
91 | actual``.
92 |
93 | 1. Otherwise, for all objects, including arrays, they are
94 | deeply equal only if they have the same number of owned,
95 | enumerable properties, the same property names (although
96 | not necessarily in the same order), and the respective
97 | values for each key are deeply equal.
98 |
99 | 1. The assertion object must have a function
100 | ``notDeepEqual(actual, unexpected, message_opt)``
101 |
102 | 1. The corresponding ``operator`` for an ``AssertionError`` is
103 | ``"notDeepEqual"``.
104 |
105 | 1. The criterion for ``notDeepEqual`` is that the actual and
106 | expected values do not meet the criterion of ``deepEqual``.
107 |
108 | 1. The assertion object must have a function
109 | ``strictEqual(actual, expected, message_opt)``
110 |
111 | 1. The criterion of ``strictEqual`` is that ``expected ===
112 | actual``.
113 |
114 | 1. The corresponding ``operator`` for an ``AssertionError`` is
115 | ``"==="``.
116 |
117 | 1. The assertion object must have a function
118 | ``notStrictEqual(actual, expected, message_opt)``
119 |
120 | 1. The criterion of ``notStrictEqual`` is that ``expected !==
121 | actual``.
122 |
123 | 1. The corresponding ``operator`` for an ``AssertionError`` is
124 | ``"!=="``.
125 |
126 | 1. The assertion object must have a function
127 | ``error(callback, Error_opt, message_opt)``
128 | *Note: in the CommonJS specification, this was named ``throws``,
129 | however this name was not ergonomic before ECMAScript 5.
130 | Assertion objects may provide ``throws`` for backward
131 | compatibility with that specification provided that they note that
132 | it is deprecated.*
133 |
134 | 1. The criterion for ``error`` is
135 |
136 | 1. If ``Error`` is provided, calling ``callback`` must throw
137 | an error that is an instance of the given ``Error``.
138 |
139 | 1. If ``Error`` is not provided, calling ``callback`` must
140 | throw an exception.
141 |
142 | 1. The corresponding ``operator`` for an ``AssertionError`` is
143 | ``"throws"``.
144 |
145 | 1. The corresponding ``actual`` value for an ``AssertionError``
146 | is ``undefined`` if callback does not throw an error.
147 |
148 | 1. The corresponding ``actual`` value for an ``AssertionError``
149 | is the exception thrown if the callback does throw an error.
150 |
151 | 1. The corresponding ``expected`` value for an ``AssertionError``
152 | is the global ``Error`` if the ``Error`` argument is undefined.
153 |
154 | 1. The corresponding ``expected`` value for an ``AssertionError``
155 | is the given ``Error`` argument if defined.
156 |
157 | 1. Custom assertion objects may provide additional assertion
158 | functions. *Note: to use an alternate assertion object for logging,
159 | a test object must specify an ``Assert`` constructor property*
160 |
161 |
162 | Tests
163 | =====
164 |
165 | 1. A test function,
166 |
167 | 1. A test function may accept an assertion object as its first
168 | argument, indicating that the test may log results rather than
169 | fail at the first exception.
170 |
171 | 1. A test function of ``length`` 2 must accept a completion
172 | function, indicating that the test will end when the
173 | test calls the completion function.
174 |
175 | 1. If a test function does not have ``length`` 2, it may return a
176 | promise to complete.
177 |
178 | 1. The completion value may be undefined.
179 |
180 | 1. A test object,
181 |
182 | 1. May have an ``Assert`` constructor function for providing
183 | custom assertions in the context of contained tests.
184 |
185 | 1. All owned, enumerable properties of a test object that have
186 | names that start with ``"test"`` must be test objects or
187 | functions.
188 |
189 |
190 | Runner
191 | ======
192 |
193 | *Note: by convention, the ``"test"`` module in a package is reserved
194 | for a test runner.*
195 |
196 | 1. A test runner object must have a function
197 | ``run(tests, log_opt)``
198 |
199 | 1. If ``log`` is undefined, ``run`` must construct a
200 | default logger object.
201 |
202 | 1. If ``tests`` has an ``Assert`` constructor function property,
203 | ``run`` must use this function to create assertion objects.
204 |
205 | 1. For each enumerable property of ``tests`` that starts with
206 | ``"test"``, ``run`` must execute the corresponding test,
207 | proceeding to the next test only when the previous ends.
208 |
209 | 1. If the corresponding value is a function,
210 |
211 | 1. **Prepare:** The test runner must create a logger
212 | object by calling
213 | ``log.section()``.
214 |
215 | 1. The test runner must create an assertion object and
216 | connect its ``pass`` and ``fail`` function properties
217 | to the ``pass`` and ``fail`` properties of the
218 | constructed logger.
219 |
220 | 1. **Run:** The test runner must call the test
221 | function with an assertion object and a completion
222 | function.
223 |
224 | 1. **End:** If a test function does not return a
225 | promise, the test ends upon returning.
226 |
227 | 1. If the test function returns a promise, the test ends
228 | when the promise is resolved.
229 | *Note: resolution is either fulfillment or rejection.*
230 |
231 | 1. If the ``length`` of the test function is ``2``, the
232 | test must eventually call the completion function and
233 | must not return a promise.
234 |
235 | 1. The test ends when the test calls the completion
236 | function.
237 |
238 | 1. **Fail:** If the test function throws an
239 | exception, or if the promise returned by the test
240 | function is rejected,
241 |
242 | 1. If that exception (or reason for rejection) is an
243 | instance of the ``AssertionError`` in the
244 | assertion object, the runner must call the
245 | ``fail`` function of ``log`` with the exception.
246 |
247 | 1. Otherwise, the runner must call the ``error``
248 | function of the ``log`` with the given exception
249 | or reason for rejection.
250 |
251 | 1. **Pass:** If a test ends without throwing an
252 | exception or rejecting the returned promise, the
253 | runner must call the ``pass`` function of the ``log``
254 | object with the key of the test function property.
255 |
256 | 1. If the corresponding value is an object,
257 |
258 | 1. ``run`` must create a new logger by calling
259 | ``log.section()``.
260 |
261 | 1. ``run`` must call itself with the test and logger
262 | object.
263 |
264 | 1. ``run`` must return a promise.
265 |
266 | 1. The promise must be fulfilled when the last contained test
267 | ends.
268 |
269 | 1. The last contained test of a test object with no
270 | properties is already ended.
271 |
272 | Log
273 | ===
274 |
275 | 1. A logger object must have a ``pass`` function property.
276 |
277 | 1. A logger object must have a ``fail`` function property.
278 |
279 | 1. A logger object must have an ``error`` function property.
280 |
281 | 1. A logger object must have a ``section`` function property that,
282 | when called, returns a new logger object.
283 |
284 |
--------------------------------------------------------------------------------
/tests/test.js:
--------------------------------------------------------------------------------
1 |
2 | // From Node.js test/mjsunit/test-assert.js
3 | // Felix Geisendörfer (felixge), backported from NodeJS
4 | // Karl Guertin (greyrest), backported from NodeJS
5 | // Kris Kowal (kriskowal), conversion to CommonJS
6 |
7 | // strangely meta, no?
8 |
9 | var assert = require('assert');
10 |
11 | function makeBlock(f) {
12 | var args = Array.prototype.slice.call(arguments,1);
13 | return function(){
14 | return f.apply(this, args);
15 | }
16 | }
17 |
18 | exports['test AssertionError instanceof Error'] = function () {
19 | assert.ok(new assert.AssertionError({}) instanceof Error);
20 | };
21 |
22 | exports['test ok false'] = function () {
23 | assert.error(makeBlock(assert.ok, false), assert.AssertionError);
24 | };
25 |
26 | exports['test ok(true)'] = makeBlock(assert.ok, true);
27 | exports['test ok("test")'] = makeBlock(assert.ok, "test");
28 | exports['test equal true false'] = function () {
29 | assert.error(makeBlock(assert.equal, true, false), assert.AssertionError, 'equal');
30 | };
31 |
32 | exports['test equal null null'] = makeBlock(assert.equal, null, null);
33 | exports['test equal undefined undefined'] = makeBlock(assert.equal, undefined, undefined);
34 | exports['test equal null undefined'] = makeBlock(assert.equal, null, undefined);
35 | exports['test equal 2 "2"'] = makeBlock(assert.equal, 2, "2");
36 | exports['test equal "2" 2'] = makeBlock(assert.equal, "2", 2);
37 | exports['test equal true true'] = makeBlock(assert.equal, true, true);
38 | exports['test notEqual true false'] = makeBlock(assert.notEqual, true, false);
39 | exports['test notEqual true true'] = function () {
40 | assert.error(makeBlock(assert.notEqual, true, true), assert.AssertionError, 'notEqual');
41 | };
42 | exports['test strictEqual 2 "2"'] = function () {
43 | assert.error(makeBlock(assert.strictEqual, 2, "2"), assert.AssertionError, 'strictEqual');
44 | };
45 | exports['test strictEqual null undefined'] = function () {
46 | assert.error(makeBlock(assert.strictEqual, null, undefined), assert.AssertionError, 'strictEqual');
47 | };
48 | exports['test notStrictEqual 2 "2"'] = makeBlock(assert.notStrictEqual, 2, "2");
49 |
50 | //deepEquals
51 |
52 | exports['test deepEqual date'] = makeBlock(assert.deepEqual, new Date(2000,3,14), new Date(2000,3,14));
53 | exports['test deepEqual date negative'] = function () {
54 | assert.error(makeBlock(assert.deepEqual, new Date(), new Date(2000,3,14)), assert.AssertionError, 'deepEqual date');
55 | };
56 |
57 | exports['test deepEqual 4 "4"'] = makeBlock(assert.deepEqual, 4, "4");
58 | exports['test deepEqual "4" 4'] = makeBlock(assert.deepEqual, "4", 4);
59 | exports['test deepEqual true 1'] = makeBlock(assert.deepEqual, true, 1);
60 | exports['test deepEqual 4 "5"'] = function () {
61 | assert.error(makeBlock(assert.deepEqual, 4, "5"));
62 | };
63 |
64 | // having the same number of owned properties && the same set of keys
65 | exports['test deepEqual {a:4} {a:4}'] = makeBlock(assert.deepEqual, {a:4}, {a:4});
66 | exports['test deepEqual {a:4,b:"2"} {a:4,b:"2"}'] = makeBlock(assert.deepEqual, {a:4,b:"2"}, {a:4,b:"2"});
67 | exports['test deepEqual [4] ["4"]'] = makeBlock(assert.deepEqual, [4], ["4"]);
68 | exports['test deepEqual {a:4} {a:4,b:true}'] = function () {
69 | assert.error(makeBlock(assert.deepEqual, {a:4}, {a:4,b:true}), assert.AssertionError);
70 | };
71 |
72 | exports['test deepEqual ["a"], {0:"a"}'] = makeBlock(assert.deepEqual, ["a"], {0:"a"});
73 | //(although not necessarily the same order),
74 | exports['test deepEqual {a:4,b:"1"} {b:"1",a:4}'] = makeBlock(assert.deepEqual, {a:4,b:"1"}, {b:"1",a:4});
75 |
76 | exports['test deepEqual arrays with non-numeric properties'] = function () {
77 | var a1 = [1,2,3];
78 | var a2 = [1,2,3];
79 | a1.a = "test";
80 | a1.b = true;
81 | a2.b = true;
82 | a2.a = "test"
83 | assert.error(makeBlock(assert.deepEqual, Object.keys(a1), Object.keys(a2)), assert.AssertionError);
84 | makeBlock(assert.deepEqual, a1, a2);
85 | };
86 |
87 | exports['test deepEqual identical prototype'] = function () {
88 | // having an identical prototype property
89 | var nbRoot = {
90 | toString: function(){return this.first+' '+this.last;}
91 | }
92 | var nameBuilder = function(first,last){
93 | this.first = first;
94 | this.last = last;
95 | return this;
96 | }
97 | nameBuilder.prototype = nbRoot;
98 | var nameBuilder2 = function(first,last){
99 | this.first = first;
100 | this.last = last;
101 | return this;
102 | }
103 | nameBuilder2.prototype = nbRoot;
104 | var nb1 = new nameBuilder('Ryan', 'Dahl');
105 | var nb2 = new nameBuilder2('Ryan', 'Dahl');
106 |
107 | assert.deepEqual(nb1, nb2);
108 |
109 | nameBuilder2.prototype = Object;
110 | nb2 = new nameBuilder2('Ryan', 'Dahl');
111 | assert.error(makeBlock(assert.deepEqual, nb1, nb2), assert.AssertionError);
112 |
113 | };
114 |
115 | exports['test deepEqual "a" {}'] = function () {
116 | assert.error(makeBlock(assert.deepEqual, 'a', {}), assert.AssertionError);
117 | };
118 |
119 | exports['test deepEqual "" ""'] = function () {
120 | assert.deepEqual("", "");
121 | };
122 |
123 | exports['test deepEqual "" [""]'] = function () {
124 | assert.error(makeBlock(assert.deepEqual, '', ['']), assert.AssertionError);
125 | };
126 |
127 | exports['test deepEqual [""] [""]'] = function () {
128 | assert.deepEqual([""], [""]);
129 | };
130 |
131 | exports['test throw AssertionError'] = function () {
132 |
133 | //Testing the throwing
134 | function thrower(errorConstructor){
135 | throw new errorConstructor('test');
136 | }
137 | var aethrow = makeBlock(thrower, assert.AssertionError);
138 | var aethrow = makeBlock(thrower, assert.AssertionError);
139 | //the basic calls work
140 | assert.error(makeBlock(thrower, assert.AssertionError), assert.AssertionError, 'message');
141 | assert.error(makeBlock(thrower, assert.AssertionError), assert.AssertionError);
142 | assert.error(makeBlock(thrower, assert.AssertionError));
143 | //if not passing an error, catch all.
144 | assert.error(makeBlock(thrower, TypeError));
145 | //when passing a type, only catch errors of the appropriate type
146 | var threw = false;
147 | try {
148 | assert.error(makeBlock(thrower, TypeError), assert.AssertionError);
149 | } catch (e) {
150 | threw = true;
151 | assert.ok(e instanceof TypeError, 'type');
152 | }
153 | assert.ok(threw, 'assert.throws with an explicit error is eating extra errors', assert.AssertionError);
154 | threw = false;
155 |
156 | };
157 |
158 | if (module == require.main)
159 | require("test").run(exports);
160 |
161 |
--------------------------------------------------------------------------------
/tests/tutorial.md:
--------------------------------------------------------------------------------
1 |
2 | ### A typical test, failing at the first failed assertion
3 |
4 | Having arranged for "assert" and "test" to be modules in my package
5 | for assertions and running tests, I create a test function and run it
6 | if the module is executed as the main module.
7 |
8 | var assert = require("assert");
9 |
10 | function test() {
11 | assert.ok(false, "it worked"); // it doesn't
12 | // but we don't get here
13 | assert.equal(1, 1, "they're equal");
14 | // and they would have been, but they
15 | // didn't get a chance.
16 | }
17 |
18 | if (require.main === module)
19 | require("test").run(test);
20 |
21 | Depending on the default logger, this might look like:
22 |
23 | FAIL it worked
24 | 0 passes, 1 failure, 0 errors
25 |
26 |
27 | ### A test that logs passes and fails instead of stopping at the first failed assertion
28 |
29 | exports['test foo'] = function (assert) {
30 | assert.ok(false, "it worked"); // it doesn't
31 | // and we log it, moving on...
32 | assert.equal(1, 1, "they're equal");
33 | // and they are, and we log that too
34 | };
35 |
36 | if (require.main === module)
37 | require("test").run(exports);
38 |
39 | Again, depending on the logger, it might look like:
40 |
41 | FAIL it worked
42 | PASS they're equal
43 | 0 passes, 2 failures, 0 errors
44 |
45 |
46 | ### Composing test suites with modules.
47 |
48 | I can now create a test suite as a module by requiring the foo test as
49 | part of mine.
50 |
51 | exports['test foo'] = require("./foo");
52 | exports['test bar'] = require("./bar");
53 |
54 | if (require.main === module)
55 | require("test").run(exports);
56 |
57 | Since the ``"foo"`` test module also checks whether it's the main
58 | module before executing, we will only invoke the test runner once.
59 |
60 |
61 | ### An asynchronous test using a completion function
62 |
63 | Relying on the accuracy of ``setTimeout``, we'll test the accuracy of
64 | ``inABit(callback)``. We'll accept the assertion module as a first
65 | argument and call the ``done`` function to end the test.
66 |
67 | var tolerance = 100; // miliseconds
68 | exports['test in a bit'] = function (assert, done) {
69 | var called = false;
70 | inABit(function () {
71 | called = true;
72 | });
73 | setTimeout(function () {
74 | assert.ok(called, "called one second later");
75 | done();
76 | }, 1000 + tolerance);
77 | };
78 |
79 |
80 | ### An asynchronous test using a promise
81 |
82 | var Q = require("q");
83 |
84 | var inABit = function () {
85 | var deferred = Q.defer();
86 | setTimeout(deferred.resolve, 1000);
87 | return deferred.promise;
88 | }
89 |
90 | var tolerance = 100; // miliseconds
91 |
92 | exports['test a promise'] = function () {
93 | var start = +new Date();
94 | return Q.when(inABit(), function () {
95 | var stop = +new Date();
96 | var duration = stop - start;
97 | assert.ok(duration < 1000 + tolerance, 'within tolerance');
98 | });
99 | };
100 |
101 |
102 | ### A set of tests with custom assertion functions
103 |
104 | var assert = require("assert");
105 |
106 | exports.Assert = function () {
107 | var self = Object.create(assert);
108 | self.inRange = function (actual, min, max, message) {
109 | var ok = actual >= min && actual <= max;
110 | if (ok) {
111 | self.pass(message);
112 | } else {
113 | self.fail({
114 | message: message,
115 | actual: actual,
116 | expected: "between " + min + " and " + max,
117 | operator: "inRange"
118 | });
119 | }
120 | };
121 | return self;
122 | };
123 |
124 | var tolerance = 100;
125 | exports['test a promise'] = function () {
126 | var inABit = require("in-a-bit").inABit;
127 | var start = +new Date();
128 | return Q.when(inABit(), function () {
129 | var stop = +new Date();
130 | var duration = stop - start;
131 | var min = 1000 - tolerance;
132 | var max = 1000 + tolerance;
133 | assert.inRange(duration, min, max, 'within tolerance');
134 | });
135 | };
136 |
137 |
138 | ### Running a test with a custom logger
139 |
140 | var Logger = function () {
141 | var self = {
142 | passes: 0,
143 | failures: 0,
144 | errors: 0,
145 | pass: function (message) {
146 | console.log('PASS', message);
147 | root.passes++;
148 | },
149 | fail: function (assertion) {
150 | console.error(assertion);
151 | root.failures++;
152 | },
153 | error: function (exception) {
154 | console.error(exception);
155 | root.errors++;
156 | },
157 | section: function () {
158 | return self;
159 | },
160 | report: function () {
161 | console.log(
162 | self.passes, 'passes',
163 | self.failures, 'failures',
164 | self.errors, 'errors'
165 | );
166 | }
167 | };
168 | return self;
169 | };
170 |
171 | if (require.main === module) {
172 | var log = Logger();
173 | var allSpecs = require("./all");
174 | require("test").run(allSpecs, Logger())
175 | log.report();
176 | }
177 |
178 |
179 | ### Running a module as a test.
180 |
181 | exports['test foo'] = require.bind(null, './foo-spec');
182 |
183 | if (require.main === module)
184 | require("test").run(exports);
185 |
186 |
--------------------------------------------------------------------------------