Core
At the centre of Gruber are a set of agnostic modules to help create platform-independent JavaScript servers. They are roughly organised as:
- Container is a dependency management utility to capture code dependencies, provide them on demand and allow them to be replaced during testing.
- Migrator is a helper for structuring project migrations that run up or down to manage the state of something like a database.
- Miscellaneous contains various things that are often useful
- Store is an abstraction for interacting with asynchonous key-value storage
- Terminator is a utility for ensuring graceful shutdown of your apps, especially when behind a load-balancer
- Tokens are an abstraction around signing access to a user to be consumed by other parts of the application
Container
Dependencies
type
A utility type for defining a record of dependency-factories
UnwrapDependencies
type
A utility type for converting a record of dependency-factories to dependencies
WrapDependencies
type
A utility type to convert dependencies back into dependency-factories
Container
unstable
Container holds a set of dependencies that are lazily generated from their factories and provides a system to override those dependencies for testing
const container = new Container({
message: () => 'hello there',
store: useStore
})
// Retrieve a dependency
console.log(container.get('message')) // outputs "hello there"
// Override dependencies
container.override({
store: new MemoryStore()
})
// get the overridden store
let store = container.get('store') // MemoryStore
// attempt to get the message
container.get('message') // throws Error('unmet dependency')
// restore the container back to the original dependencies
container.reset()
get
Get a dependency. First checking overrides, then previously computed or finaly use the dependency factory
override
Override the dependencies within the container or create unmet dependencies for those not-provided
// Replace the store with an in-memory one
container.override({ store: new MemoryStore() })
proxy
Create a proxy around an object that injects our dependencies
const container = new Container({ message: () => 'hello there' })
const proxy = container.proxy({ count: 7 })
proxy.message // 'hello there'
proxy.count // 7
// or with object destructuring
const { message, count } = container.proxy({ count: 7 })
reset
Clear any overrides on the dependencies
container.reset()
unwrap
internal
Compute a dependency from it's factory
const message = container.unwrap('message')
Migrator
defineMigration
Define a generic migration, this is a generic wrapper around creating a MigrationOptions
which within TypeScript you can specify the <T>
once, rather than for each action.
const migration = defineMigration({
up () {},
down () {},
})
loadMigration
Attempt to load a migration from a file using import
.
It combines the name
and directory
to get a file path, attempts to import
-it and convert the default
export into a MigrationDefinition
. You can also force the <T>
parameter onto the definition.
It will throw errors if the file does not exist or if the default export doesn't look like a MigrationOptions
.
const migration = await loadMigration(
'001-create-users.js',
new URL('./migrations/', import.meta.url)
)
migration.name // "001-create-users.js"
migration.up // function
migration.down // function
MigrationOptions
type
Options for defining some sort of migration
const options = {
up(value) {},
down(value) {},
}
MigrationDefinition
type
A definition for a migration, it's basically a MigrationOptions
with a unique name
const migration = {
name: '042-some-migration',
up() {},
down() {},
}
MigrationRecord
type
A record of a migration that has been performed
const record = { name: '042-some-migration' }
Migrator
Migrator provides methods for running a specific type of migrations. The idea is that different platforms/integrations can create a migrator that works with a specific feature they want to add migrations around, e.g. a Postgres database.
async function getRecords() {}
async function getDefinitions() {}
async function execute() {}
const migrator = new Migrator({ getRecords, getDefinitions, execute })
down
Run any "down" migrations for migrations that have already been performed
It would be cool to specify a number here so you could run just 1 but I haven't needed this so it hasn't been properly designed yet
await migrator.up()
up
Run any pending "up" migrations
It would be cool to specify a number here so you could run just 1 but I haven't needed this so it hasn't been properly designed yet
await migrator.up()
Miscellaneous
RandomService
type
RandomService provices an abstraction around generating random values
const random // RandomService
// Pick a number between 4 & 7 inclusively
let number = random.number(4, 7)
// Generate a UUID
let uuid = random.uuid()
// Pick an element from an array
let element = random.element([1, 2, 3, 4, 5])
useRandom
A standard implementation of RandomService
using Math.random + crypto.randomUUID()
const random = useRandom()
let number = random.number(4, 7)
let uuid = random.uuid()
let element = random.element([1, 2, 3, 4, 5])
formatMarkdownTable
Given a set of records
with known columns
, format them into a pretty markdown table using the order from columns
.
If a record does not have a specified value (it is null or undefined) it will be replaced with the fallback
value.
const table = formatMarkdownTable(
[
{ name: 'Geoff Testington', age: 42 },
{ name: "Jess Smith", age: 32 },
{ name: "Tyler Rockwell" },
],
['name', 'age'],
'~'
)
Which will generate:
| name | age |
| ---------------- | --- |
| Geoff Testington | 42 |
| Jess Smith | 32 |
| Tyler Rockwell | ~ |
loader
unstable
loader
let's you memoize the result of a function to create a singleton from it.
It works synchronously or with promises.
let index = 1
const useMessage = loader(() = 'hello there ${i++}')
useMessage() // hello there 1
useMessage() // hello there 1
useMessage() // hello there 1
trimIndentation
trimIndentation
takes a template literal (with values) and takes out the common whitespace.
Very heavily based on dedent
import { trimIndentation } from "gruber";
console.log(
trimIndentation`
Hello there!
My name is Geoff
`,
);
Which will output this, without any extra whitespace:
Hello there!
My name is Geoff
preventExtraction
Take steps to prevent an object from being extracted from the app, inspired by crypto.subtle.importKey's extractable parameter.
This will:
- throw an error if the value are passed to JSON.stringify
- it recursively applies to nested objects, arrays and items within arrays
- seal and freeze the value and all nested objects & arrays
const config = preventExtraction({
name: "Geoff Testington",
pets: [
{ name: "Hugo" },
{ name: "Helga" },
],
favourite: {
mountain: "Cheviot"
}
})
// Any attempt to JSON-ify will result in an error
console.log(JSON.stringify(config)) // throws a TypeError
console.log(JSON.stringify(config.pets)) // throws a TypeError
console.log(JSON.stringify(config.pets[0])) // throws a TypeError
console.log(JSON.stringify(config.pets[1])) // throws a TypeError
console.log(JSON.stringify(config.favourite)) // throws a TypeError
The value will also be frozen and sealed, so any properties cannot be added, removed or modified.
dangerouslyExpose
unstable
DANGER undo a preventExtraction to allow values to be exposed.
This undos all of the precations that preventExtraction
add.
PromiseList
A dynamic list of promises that are automatically removed when they resolve
const list = new PromiseList()
// Add a promise that waits for 5 seconds
list.push(async () => {
await new Promise(r => setTimeout(r, 5_000))
// Add dependant promises too
list.push(async () => {
await somethingElse()
})
})
// Wait for all promises and dependants to resolve in one go
await promises.all()
all
Wait for all promises to be resolved using Promise.all
.
If new promises are added as a result of waiting, they are also awaited.
await list.all()
length
Get the current number of promises in the list
list.length // 5
push
Add a promise to the list using a factory method,
the factory
just needs to return a promise
list.push(async () => {
// ...
})
Store
Store
type
Store is an async abstraction around a key-value engine like Redis or a JavaScript Map with extra features for storing things for set-durations
const store // Store
// Store Geoff for 5 minutes
await store.set(
'users/geoff',
{ name: "Geoff Testington"},
{ maxAge: 5 * 60 * 1_000 }
)
// Retrieve Geoff
const value = await store.get('users/geoff')
// Remove Geoff
await store.delete('users/geoff')
// Close the store
await store.dispose()
// Use Explicit Resource Management to automatically dispose the store
async function main() {
using store = new MemoryStore(...)
await store.set('users/geoff', ...)
}
MemoryStore
MemoryStore is a in-memory implementation of Store that puts values into a Map and uses timers to expire data. It was mainly made for automated testing.
const store = new MemoryStore()
Terminator
TerminatorOptions
internal
type
Options for creating a Terminator instance
const options = {
// How long to wait in the terminating state so loadbalancers can process it
timeout: 5_000,
// Which OS signals to listen for
signals: ['SIGINT', 'SIGTERM'],
// Register each signal with the OS and call the handler
startListeners(signals, handler) {},
// Exit the process with a given code and optionaly log an error
exitProcess(statusCode, error) {},
}
Terminator
internal
Terminators let you add graceful shutdown to your applications, create one with TerminatorOptions
const arnie = new Terminator({
timeout: 5_000,
signals: ['SIGINT', 'SIGTERM'],
startListeners(signals, handler) {},
exitProcess(statusCode, error) {},
})
getResponse
Get a Fetch Response with the state of the terminator, probably for a load balancer.
If the terminator is running, it will return a http/200 otherwise it will return a http/503
const response = await arnie.getResponse()
start
Start the terminator and capture a block of code to close the server
arnie.start(async () => {
await store.dispose()
})
terminate
internal
Start the shutdown process
await arnie.terminate(async () => {
await store.dispose()
})
Tokens
TokenService
unstable
type
A service for signing and verifying access tokens
CompositeTokens
unstable
A TokenService with multiple verification methods and a single signer
debug
{ "Dependencies": { "entrypoint": "core/mod.ts", "id": "Dependencies", "name": "Dependencies", "content": "A utility type for defining a record of dependency-factories", "tags": { "group": "Container", "type": "true" }, "children": {} }, "UnwrapDependencies": { "entrypoint": "core/mod.ts", "id": "UnwrapDependencies", "name": "UnwrapDependencies", "content": "A utility type for converting a record of dependency-factories to dependencies", "tags": { "group": "Container", "type": "true" }, "children": {} }, "WrapDependencies": { "entrypoint": "core/mod.ts", "id": "WrapDependencies", "name": "WrapDependencies", "content": "A utility type to convert dependencies back into dependency-factories", "tags": { "group": "Container", "type": "true" }, "children": {} }, "Container": { "entrypoint": "core/mod.ts", "id": "Container", "name": "Container", "content": "Container holds a set of dependencies that are lazily generated from their factories\nand provides a system to override those dependencies for testing\n\n```js\nconst container = new Container({\n message: () => 'hello there',\n store: useStore\n})\n\n// Retrieve a dependency\nconsole.log(container.get('message')) // outputs \"hello there\"\n\n// Override dependencies\ncontainer.override({\n store: new MemoryStore()\n})\n\n// get the overridden store\nlet store = container.get('store') // MemoryStore\n\n// attempt to get the message\ncontainer.get('message') // throws Error('unmet dependency')\n\n// restore the container back to the original dependencies\ncontainer.reset()\n```", "tags": { "unstable": "true", "group": "Container" }, "children": { "override": { "id": "Container#override", "name": "override", "content": "Override the dependencies within the container or create unmet dependencies for those not-provided\n\n```js\n// Replace the store with an in-memory one\ncontainer.override({ store: new MemoryStore() })\n```", "tags": { "group": "Miscellaneous" }, "children": {} }, "reset": { "id": "Container#reset", "name": "reset", "content": "Clear any overrides on the dependencies\n\n```js\ncontainer.reset()\n```", "tags": { "group": "Miscellaneous" }, "children": {} }, "get": { "id": "Container#get", "name": "get", "content": "Get a dependency. First checking overrides, then previously computed or finaly use the dependency factory", "tags": { "group": "Miscellaneous" }, "children": {} }, "unwrap": { "id": "Container#unwrap", "name": "unwrap", "content": "Compute a dependency from it's factory\n\n```js\nconst message = container.unwrap('message')\n```", "tags": { "internal": "true", "group": "Miscellaneous" }, "children": {} }, "proxy": { "id": "Container#proxy", "name": "proxy", "content": "Create a proxy around an object that injects our dependencies\n\n```ts\nconst container = new Container({ message: () => 'hello there' })\n\nconst proxy = container.proxy({ count: 7 })\nproxy.message // 'hello there'\nproxy.count // 7\n\n// or with object destructuring\nconst { message, count } = container.proxy({ count: 7 })\n```", "tags": { "group": "Miscellaneous" }, "children": {} } } }, "defineMigration": { "entrypoint": "core/mod.ts", "id": "defineMigration", "name": "defineMigration", "content": "Define a generic migration, this is a generic wrapper around creating a `MigrationOptions`\nwhich within TypeScript you can specify the `` once, rather than for each action.\n\n```js\nconst migration = defineMigration({\n up () {},\n down () {},\n})\n```", "tags": { "group": "Migrator" }, "children": {} }, "loadMigration": { "entrypoint": "core/mod.ts", "id": "loadMigration", "name": "loadMigration", "content": "Attempt to load a migration from a file using `import`.\n\nIt combines the `name` and `directory` to get a file path, attempts to `import`-it and convert the `default` export into a `MigrationDefinition`. You can also force the ` ` parameter onto the definition.\n\nIt will throw errors if the file does not exist or if the default export doesn't look like a `MigrationOptions`.\n\n\n```js\nconst migration = await loadMigration(\n '001-create-users.js',\n new URL('./migrations/', import.meta.url)\n)\n\nmigration.name // \"001-create-users.js\"\nmigration.up // function\nmigration.down // function\n```", "tags": { "group": "Migrator" }, "children": {} }, "MigrationOptions": { "entrypoint": "core/mod.ts", "id": "MigrationOptions", "name": "MigrationOptions", "content": "Options for defining some sort of migration\n\n```js\nconst options = {\n up(value) {},\n down(value) {},\n}\n```", "tags": { "group": "Migrator", "type": "true" }, "children": {} }, "MigrationDefinition": { "entrypoint": "core/mod.ts", "id": "MigrationDefinition", "name": "MigrationDefinition", "content": "A definition for a migration, it's basically a `MigrationOptions` with a unique name\n\n```js\nconst migration = {\n name: '042-some-migration',\n up() {},\n down() {},\n}\n```", "tags": { "group": "Migrator", "type": "true" }, "children": {} }, "MigrationRecord": { "entrypoint": "core/mod.ts", "id": "MigrationRecord", "name": "MigrationRecord", "content": "A record of a migration that has been performed\n\n```js\nconst record = { name: '042-some-migration' }\n```", "tags": { "group": "Migrator", "type": "true" }, "children": {} }, "Migrator": { "entrypoint": "core/mod.ts", "id": "Migrator", "name": "Migrator", "content": "Migrator provides methods for running a specific type of migrations.\nThe idea is that different platforms/integrations can create a migrator that\nworks with a specific feature they want to add migrations around, e.g. a Postgres database.\n\n```js\nasync function getRecords() {}\n\nasync function getDefinitions() {}\n\nasync function execute() {}\n\nconst migrator = new Migrator({ getRecords, getDefinitions, execute })\n```\n\nSee [examples/node-fs-migrator](/examples/node-fs-migrator/node-fs-migrator.js)", "tags": { "group": "Migrator" }, "children": { "up": { "id": "Migrator#up", "name": "up", "content": "Run any pending \"up\" migrations\n\n> It would be cool to specify a number here so you could run just 1 but\n> I haven't needed this so it hasn't been properly designed yet\n\n```js\nawait migrator.up()\n```", "tags": { "group": "Miscellaneous" }, "children": {} }, "down": { "id": "Migrator#down", "name": "down", "content": "Run any \"down\" migrations for migrations that have already been performed\n\n> It would be cool to specify a number here so you could run just 1 but\n> I haven't needed this so it hasn't been properly designed yet\n\n```js\nawait migrator.up()\n```", "tags": { "group": "Miscellaneous" }, "children": {} } } }, "RandomService": { "entrypoint": "core/mod.ts", "id": "RandomService", "name": "RandomService", "content": "RandomService provices an abstraction around generating random values\n\n```js\nconst random // RandomService\n\n// Pick a number between 4 & 7 inclusively\nlet number = random.number(4, 7)\n\n// Generate a UUID\nlet uuid = random.uuid()\n\n// Pick an element from an array\nlet element = random.element([1, 2, 3, 4, 5])\n```", "tags": { "group": "Miscellaneous", "type": "true" }, "children": {} }, "useRandom": { "entrypoint": "core/mod.ts", "id": "useRandom", "name": "useRandom", "content": "A standard implementation of `RandomService` using Math.random + crypto.randomUUID()\n\n```js\nconst random = useRandom()\nlet number = random.number(4, 7)\nlet uuid = random.uuid()\nlet element = random.element([1, 2, 3, 4, 5])\n```", "tags": { "group": "Miscellaneous" }, "children": {} }, "Store": { "entrypoint": "core/mod.ts", "id": "Store", "name": "Store", "content": "Store is an async abstraction around a key-value engine like Redis or a JavaScript Map\nwith extra features for storing things for set-durations\n\n```js\nconst store // Store\n\n// Store Geoff for 5 minutes\nawait store.set(\n 'users/geoff',\n { name: \"Geoff Testington\"},\n { maxAge: 5 * 60 * 1_000 }\n)\n\n// Retrieve Geoff\nconst value = await store.get('users/geoff')\n\n// Remove Geoff\nawait store.delete('users/geoff')\n\n// Close the store\nawait store.dispose()\n\n// Use Explicit Resource Management to automatically dispose the store\nasync function main() {\n using store = new MemoryStore(...)\n\n await store.set('users/geoff', ...)\n}\n```", "tags": { "group": "Store", "type": "true" }, "children": {} }, "MemoryStore": { "entrypoint": "core/mod.ts", "id": "MemoryStore", "name": "MemoryStore", "content": "MemoryStore is a in-memory implementation of [Store](#store) that puts values into a Map and uses timers to expire data.\nIt was mainly made for automated testing.\n\n```js\nconst store = new MemoryStore()\n```", "tags": { "group": "Store" }, "children": {} }, "TerminatorOptions": { "entrypoint": "core/mod.ts", "id": "TerminatorOptions", "name": "TerminatorOptions", "content": "Options for creating a [Terminator](#terminator) instance\n\n```js\nconst options = {\n // How long to wait in the terminating state so loadbalancers can process it\n timeout: 5_000,\n\n // Which OS signals to listen for\n signals: ['SIGINT', 'SIGTERM'],\n\n // Register each signal with the OS and call the handler\n startListeners(signals, handler) {},\n\n // Exit the process with a given code and optionaly log an error\n exitProcess(statusCode, error) {},\n}\n```", "tags": { "internal": "true", "group": "Terminator", "type": "true" }, "children": {} }, "Terminator": { "entrypoint": "core/mod.ts", "id": "Terminator", "name": "Terminator", "content": "Terminators let you add graceful shutdown to your applications,\ncreate one with [TerminatorOptions](#terminatoroptions)\n\n```js\nconst arnie = new Terminator({\n timeout: 5_000,\n signals: ['SIGINT', 'SIGTERM'],\n startListeners(signals, handler) {},\n exitProcess(statusCode, error) {},\n})\n```", "tags": { "internal": "true", "group": "Terminator" }, "children": { "start": { "id": "Terminator#start", "name": "start", "content": "Start the terminator and capture a block of code to close the server\n\n```js\narnie.start(async () => {\n await store.dispose()\n})\n```", "tags": { "group": "Miscellaneous" }, "children": {} }, "terminate": { "id": "Terminator#terminate", "name": "terminate", "content": "Start the shutdown process\n\n```js\nawait arnie.terminate(async () => {\n await store.dispose()\n})\n```", "tags": { "internal": "true", "group": "Miscellaneous" }, "children": {} }, "getResponse": { "id": "Terminator#getResponse", "name": "getResponse", "content": "Get a Fetch Response with the state of the terminator, probably for a load balancer.\n\nIf the terminator is running, it will return a http/200\notherwise it will return a http/503\n\n```js\nconst response = await arnie.getResponse()\n```", "tags": { "group": "Miscellaneous" }, "children": {} } } }, "TokenService": { "entrypoint": "core/mod.ts", "id": "TokenService", "name": "TokenService", "content": "A service for signing and verifying access tokens", "tags": { "unstable": "true", "group": "Tokens", "type": "true" }, "children": {} }, "CompositeTokens": { "entrypoint": "core/mod.ts", "id": "CompositeTokens", "name": "CompositeTokens", "content": "A TokenService with multiple verification methods and a single signer", "tags": { "unstable": "true", "group": "Tokens" }, "children": {} }, "formatMarkdownTable": { "entrypoint": "core/mod.ts", "id": "formatMarkdownTable", "name": "formatMarkdownTable", "content": "Given a set of `records` with known `columns`, format them into a pretty markdown table using the order from `columns`.\nIf a record does not have a specified value (it is null or undefined) it will be replaced with the `fallback` value.\n\n```js\nconst table = formatMarkdownTable(\n\t[\n\t\t{ name: 'Geoff Testington', age: 42 },\n\t\t{ name: \"Jess Smith\", age: 32 },\n\t\t{ name: \"Tyler Rockwell\" },\n\t],\n\t['name', 'age'],\n\t'~'\n)\n```\n\nWhich will generate:\n\n```\n| name | age |\n| ---------------- | --- |\n| Geoff Testington | 42 |\n| Jess Smith | 32 |\n| Tyler Rockwell | ~ |\n```", "tags": { "group": "Miscellaneous" }, "children": {} }, "loader": { "entrypoint": "core/mod.ts", "id": "loader", "name": "loader", "content": "`loader` let's you memoize the result of a function to create a singleton from it.\nIt works synchronously or with promises.\n\n```js\nlet index = 1\nconst useMessage = loader(() = 'hello there ${i++}')\n\nuseMessage() // hello there 1\nuseMessage() // hello there 1\nuseMessage() // hello there 1\n```", "tags": { "unstable": "true", "group": "Miscellaneous" }, "children": {} }, "trimIndentation": { "entrypoint": "core/mod.ts", "id": "trimIndentation", "name": "trimIndentation", "content": "`trimIndentation` takes a template literal (with values) and takes out the common whitespace.\nVery heavily based on [dedent](https://github.com/dmnd/dedent/tree/main)\n\n```js\nimport { trimIndentation } from \"gruber\";\n\nconsole.log(\n\ttrimIndentation`\n\t\tHello there!\n\t\tMy name is Geoff\n\t`,\n);\n```\n\nWhich will output this, without any extra whitespace:\n\n```\nHello there!\nMy name is Geoff\n```", "tags": { "group": "Miscellaneous" }, "children": {} }, "preventExtraction": { "entrypoint": "core/mod.ts", "id": "preventExtraction", "name": "preventExtraction", "content": "Take steps to prevent an object from being extracted from the app,\ninspired by crypto.subtle.importKey's [extractable](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#extractable) parameter.\n\nThis will:\n- throw an error if the value are passed to JSON.stringify\n- it recursively applies to nested objects, arrays and items within arrays\n- [seal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal) and [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) the value and all nested objects & arrays\n\n```js\nconst config = preventExtraction({\n\tname: \"Geoff Testington\",\n\tpets: [\n\t\t{ name: \"Hugo\" },\n\t\t{ name: \"Helga\" },\n\t],\n favourite: {\n\t\tmountain: \"Cheviot\"\n\t}\n})\n\n// Any attempt to JSON-ify will result in an error\nconsole.log(JSON.stringify(config)) // throws a TypeError\nconsole.log(JSON.stringify(config.pets)) // throws a TypeError\nconsole.log(JSON.stringify(config.pets[0])) // throws a TypeError\nconsole.log(JSON.stringify(config.pets[1])) // throws a TypeError\nconsole.log(JSON.stringify(config.favourite)) // throws a TypeError\n```\n\nThe value will also be frozen and sealed, so any properties cannot be added, removed or modified.", "tags": { "group": "Miscellaneous" }, "children": {} }, "dangerouslyExpose": { "entrypoint": "core/mod.ts", "id": "dangerouslyExpose", "name": "dangerouslyExpose", "content": "**DANGER** undo a [preventExtraction](#preventextraction) to allow values to be exposed.\nThis undos all of the precations that `preventExtraction` add.", "tags": { "unstable": "true", "group": "Miscellaneous" }, "children": {} }, "PromiseList": { "entrypoint": "core/mod.ts", "id": "PromiseList", "name": "PromiseList", "content": "A dynamic list of promises that are automatically removed when they resolve\n\n```js\nconst list = new PromiseList()\n\n// Add a promise that waits for 5 seconds\nlist.push(async () => {\n\tawait new Promise(r => setTimeout(r, 5_000))\n\n\t// Add dependant promises too\n\tlist.push(async () => {\n\t\tawait somethingElse()\n\t})\n})\n\n// Wait for all promises and dependants to resolve in one go\nawait promises.all()\n\n```", "tags": { "group": "Miscellaneous" }, "children": { "push": { "id": "PromiseList#push", "name": "push", "content": "Add a promise to the list using a factory method,\nthe `factory` just needs to return a promise\n\n```js\nlist.push(async () => {\n // ...\n})\n```", "tags": { "group": "Miscellaneous" }, "children": {} }, "all": { "id": "PromiseList#all", "name": "all", "content": "Wait for all promises to be resolved using `Promise.all`.\nIf new promises are added as a result of waiting, they are also awaited.\n\n```js\nawait list.all()\n```", "tags": { "group": "Miscellaneous" }, "children": {} }, "length": { "id": "PromiseList#length", "name": "length", "content": "Get the current number of promises in the list\n\n```js\nlist.length // 5\n```", "tags": { "group": "Miscellaneous" }, "children": {} } } } }