Skip to content

Core: createContext, mount, update

Defined in: src/core/errors.ts:272

Thrown when synchronous inject() targets an async provider that has not resolved yet.

Async registrations (withAsyncProvider) never resolve synchronously. Use injectAsync(token)(target) and await the result, or await mountAsync()(element) first for eager singletons.

The constructor accepts either a token string or a full options bag.

tokenOrOptions

Token name or AsyncServiceNotReadyErrorOptions bag.

import { injectAsync } from '@sandlada/document-context'
const svc = await injectAsync('remote-config')(document.getElementById('app')!)

new AsyncServiceNotReadyError(tokenOrOptions): AsyncServiceNotReadyError

Defined in: src/core/errors.ts:275

Parameter Type
tokenOrOptions string | AsyncServiceNotReadyErrorOptions

AsyncServiceNotReadyError

DocumentContextError.constructor

Property Modifier Type Description Inherited from
cause? public unknown - DocumentContextError.cause
code readonly string - DocumentContextError.code
details readonly Record<string, unknown> - DocumentContextError.details
message public string - DocumentContextError.message
name public string - DocumentContextError.name
resolutionGuide? readonly string - DocumentContextError.resolutionGuide
stack? public string - DocumentContextError.stack
token readonly string - -
stackTraceLimit static number The Error.stackTraceLimit property specifies the number of stack frames collected by a stack trace (whether generated by new Error().stack or Error.captureStackTrace(obj)). The default value is 10 but may be set to any valid JavaScript number. Changes will affect any stack trace captured after the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. DocumentContextError.stackTraceLimit

toString(): string

Defined in: src/core/errors.ts:55

Returns a string representation of an object.

string

DocumentContextError.toString

static captureStackTrace(targetObject, constructorOpt?): void

Defined in: node_modules/@types/node/globals.d.ts:51

Creates a .stack property on targetObject, which when accessed returns a string representing the location in the code at which Error.captureStackTrace() was called.

const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // Similar to `new Error().stack`

The first line of the trace will be prefixed with ${myObject.name}: ${myObject.message}.

The optional constructorOpt argument accepts a function. If given, all frames above constructorOpt, including constructorOpt, will be omitted from the generated stack trace.

The constructorOpt argument is useful for hiding implementation details of error generation from the user. For instance:

function a() {
b();
}
function b() {
c();
}
function c() {
// Create an error without stack trace to avoid calculating the stack trace twice.
const { stackTraceLimit } = Error;
Error.stackTraceLimit = 0;
const error = new Error();
Error.stackTraceLimit = stackTraceLimit;
// Capture the stack trace above function b
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
throw error;
}
a();
Parameter Type
targetObject object
constructorOpt? Function

void

DocumentContextError.captureStackTrace

static isError(error): error is Error

Defined in: site/node_modules/typescript/lib/lib.esnext.error.d.ts:21

Indicates whether the argument provided is a built-in Error instance or not.

Parameter Type
error unknown

error is Error

DocumentContextError.isError

static prepareStackTrace(err, stackTraces): any

Defined in: node_modules/@types/node/globals.d.ts:55

Parameter Type
err Error
stackTraces CallSite[]

any

https://v8.dev/docs/stack-trace-api#customizing-stack-traces

DocumentContextError.prepareStackTrace


Defined in: src/core/errors.ts:112

Thrown when a dependency cycle is detected during service resolution.

Both the synchronous (inject) and asynchronous (injectAsync) pipelines maintain a resolution stack; re-entering a token already on the stack throws this error with the full dependencyPath. Break the cycle with lazy resolution (inject inside a method instead of the factory top level), by extracting shared logic into a third service, or via event decoupling.

The constructor accepts either a bare path array or a full options bag, plus an optional positional resolutionGuide shorthand.

dependencyPathOrOptions

Either the cycle path array or a CircularDependencyErrorOptions bag.

resolutionGuide

Positional guide override, used only with the array form.

import { CircularDependencyError } from '@sandlada/document-context'
throw new CircularDependencyError(['auth', 'router', 'auth'])
throw new CircularDependencyError({
dependencyPath: ['a', 'b', 'a'],
details: { token: 'a' }
})

new CircularDependencyError(dependencyPathOrOptions, resolutionGuide?): CircularDependencyError

Defined in: src/core/errors.ts:115

Parameter Type
dependencyPathOrOptions readonly string[] | CircularDependencyErrorOptions
resolutionGuide? string

CircularDependencyError

DocumentContextError.constructor

Property Modifier Type Description Inherited from
cause? public unknown - DocumentContextError.cause
code readonly string - DocumentContextError.code
dependencyPath readonly readonly string[] - -
details readonly Record<string, unknown> - DocumentContextError.details
message public string - DocumentContextError.message
name public string - DocumentContextError.name
resolutionGuide? readonly string - DocumentContextError.resolutionGuide
stack? public string - DocumentContextError.stack
stackTraceLimit static number The Error.stackTraceLimit property specifies the number of stack frames collected by a stack trace (whether generated by new Error().stack or Error.captureStackTrace(obj)). The default value is 10 but may be set to any valid JavaScript number. Changes will affect any stack trace captured after the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. DocumentContextError.stackTraceLimit

toString(): string

Defined in: src/core/errors.ts:55

Returns a string representation of an object.

string

DocumentContextError.toString

static captureStackTrace(targetObject, constructorOpt?): void

Defined in: node_modules/@types/node/globals.d.ts:51

Creates a .stack property on targetObject, which when accessed returns a string representing the location in the code at which Error.captureStackTrace() was called.

const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // Similar to `new Error().stack`

The first line of the trace will be prefixed with ${myObject.name}: ${myObject.message}.

The optional constructorOpt argument accepts a function. If given, all frames above constructorOpt, including constructorOpt, will be omitted from the generated stack trace.

The constructorOpt argument is useful for hiding implementation details of error generation from the user. For instance:

function a() {
b();
}
function b() {
c();
}
function c() {
// Create an error without stack trace to avoid calculating the stack trace twice.
const { stackTraceLimit } = Error;
Error.stackTraceLimit = 0;
const error = new Error();
Error.stackTraceLimit = stackTraceLimit;
// Capture the stack trace above function b
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
throw error;
}
a();
Parameter Type
targetObject object
constructorOpt? Function

void

DocumentContextError.captureStackTrace

static isError(error): error is Error

Defined in: site/node_modules/typescript/lib/lib.esnext.error.d.ts:21

Indicates whether the argument provided is a built-in Error instance or not.

Parameter Type
error unknown

error is Error

DocumentContextError.isError

static prepareStackTrace(err, stackTraces): any

Defined in: node_modules/@types/node/globals.d.ts:55

Parameter Type
err Error
stackTraces CallSite[]

any

https://v8.dev/docs/stack-trace-api#customizing-stack-traces

DocumentContextError.prepareStackTrace


Defined in: src/core/errors.ts:342

Reported when an operation targets a disposed ISession.

Note the runtime contract: update() / subscribe() on a disposed session silently no-op (returning false / an empty unsubscribe) rather than throwing, so dangling async closures cannot crash the page. This error class exists for explicit diagnostic paths and custom guards that choose to surface disposal loudly.

The constructor accepts a session-key string, an options bag, or nothing.

sessionKeyOrOptions

Session key string or DisposedSessionErrorOptions bag. Optional.

import { update } from '@sandlada/document-context'
const ok = update({ count: 1 })(session)
if (!ok) {
console.warn('session already disposed, update skipped')
}

new DisposedSessionError(sessionKeyOrOptions?): DisposedSessionError

Defined in: src/core/errors.ts:345

Parameter Type
sessionKeyOrOptions? string | DisposedSessionErrorOptions

DisposedSessionError

DocumentContextError.constructor

Property Modifier Type Description Inherited from
cause? public unknown - DocumentContextError.cause
code readonly string - DocumentContextError.code
details readonly Record<string, unknown> - DocumentContextError.details
message public string - DocumentContextError.message
name public string - DocumentContextError.name
resolutionGuide? readonly string - DocumentContextError.resolutionGuide
sessionKey? readonly string - -
stack? public string - DocumentContextError.stack
stackTraceLimit static number The Error.stackTraceLimit property specifies the number of stack frames collected by a stack trace (whether generated by new Error().stack or Error.captureStackTrace(obj)). The default value is 10 but may be set to any valid JavaScript number. Changes will affect any stack trace captured after the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. DocumentContextError.stackTraceLimit

toString(): string

Defined in: src/core/errors.ts:55

Returns a string representation of an object.

string

DocumentContextError.toString

static captureStackTrace(targetObject, constructorOpt?): void

Defined in: node_modules/@types/node/globals.d.ts:51

Creates a .stack property on targetObject, which when accessed returns a string representing the location in the code at which Error.captureStackTrace() was called.

const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // Similar to `new Error().stack`

The first line of the trace will be prefixed with ${myObject.name}: ${myObject.message}.

The optional constructorOpt argument accepts a function. If given, all frames above constructorOpt, including constructorOpt, will be omitted from the generated stack trace.

The constructorOpt argument is useful for hiding implementation details of error generation from the user. For instance:

function a() {
b();
}
function b() {
c();
}
function c() {
// Create an error without stack trace to avoid calculating the stack trace twice.
const { stackTraceLimit } = Error;
Error.stackTraceLimit = 0;
const error = new Error();
Error.stackTraceLimit = stackTraceLimit;
// Capture the stack trace above function b
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
throw error;
}
a();
Parameter Type
targetObject object
constructorOpt? Function

void

DocumentContextError.captureStackTrace

static isError(error): error is Error

Defined in: site/node_modules/typescript/lib/lib.esnext.error.d.ts:21

Indicates whether the argument provided is a built-in Error instance or not.

Parameter Type
error unknown

error is Error

DocumentContextError.isError

static prepareStackTrace(err, stackTraces): any

Defined in: node_modules/@types/node/globals.d.ts:55

Parameter Type
err Error
stackTraces CallSite[]

any

https://v8.dev/docs/stack-trace-api#customizing-stack-traces

DocumentContextError.prepareStackTrace


Defined in: src/core/errors.ts:41

Abstract base class for every @sandlada/document-context exception.

Carries a stable code, a structured details bag, and an optional resolutionGuide alongside the standard Error message, name, and cause. All errors pushed to the session error stream (readErrorStream) are instances of this class. Never thrown directly; catch one of the concrete subclasses instead.

options

See DocumentContextErrorOptions.

import { readErrorStream } from '@sandlada/document-context'
readErrorStream(session).subscribe((err) => {
console.error(err.toString(), err.code, err.details)
})
  • Error

new DocumentContextError(options): DocumentContextError

Defined in: src/core/errors.ts:46

Parameter Type
options DocumentContextErrorOptions

DocumentContextError

Error.constructor

Property Modifier Type Description Inherited from
cause? public unknown - Error.cause
code readonly string - -
details readonly Record<string, unknown> - -
message public string - Error.message
name public string - Error.name
resolutionGuide? readonly string - -
stack? public string - Error.stack
stackTraceLimit static number The Error.stackTraceLimit property specifies the number of stack frames collected by a stack trace (whether generated by new Error().stack or Error.captureStackTrace(obj)). The default value is 10 but may be set to any valid JavaScript number. Changes will affect any stack trace captured after the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. Error.stackTraceLimit

toString(): string

Defined in: src/core/errors.ts:55

Returns a string representation of an object.

string

static captureStackTrace(targetObject, constructorOpt?): void

Defined in: node_modules/@types/node/globals.d.ts:51

Creates a .stack property on targetObject, which when accessed returns a string representing the location in the code at which Error.captureStackTrace() was called.

const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // Similar to `new Error().stack`

The first line of the trace will be prefixed with ${myObject.name}: ${myObject.message}.

The optional constructorOpt argument accepts a function. If given, all frames above constructorOpt, including constructorOpt, will be omitted from the generated stack trace.

The constructorOpt argument is useful for hiding implementation details of error generation from the user. For instance:

function a() {
b();
}
function b() {
c();
}
function c() {
// Create an error without stack trace to avoid calculating the stack trace twice.
const { stackTraceLimit } = Error;
Error.stackTraceLimit = 0;
const error = new Error();
Error.stackTraceLimit = stackTraceLimit;
// Capture the stack trace above function b
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
throw error;
}
a();
Parameter Type
targetObject object
constructorOpt? Function

void

Error.captureStackTrace

static isError(error): error is Error

Defined in: site/node_modules/typescript/lib/lib.esnext.error.d.ts:21

Indicates whether the argument provided is a built-in Error instance or not.

Parameter Type
error unknown

error is Error

Error.isError

static prepareStackTrace(err, stackTraces): any

Defined in: node_modules/@types/node/globals.d.ts:55

Parameter Type
err Error
stackTraces CallSite[]

any

https://v8.dev/docs/stack-trace-api#customizing-stack-traces

Error.prepareStackTrace


Defined in: src/core/errors.ts:600

Reported when server-rendered DOM, persisted storage, and blueprint seeds disagree on a property during hydration.

This is a diagnostic carrier rather than a hard failure: the configured hydrationStrategy (storageFirst by default) deterministically picks the winner, and this error shape documents the loser for logging or dev overlays. Check the strategy and verify SSR HTML matches client seeds.

The constructor accepts either (propertyKey, expectedValue, actualValue?) positionally or a full options bag.

propertyKeyOrOptions

State key or HydrationMismatchErrorOptions bag.

expectedValue

Reference value, used only with the string form.

actualValue

Applied value, used only with the string form.

import { HydrationMismatchError } from '@sandlada/document-context'
throw new HydrationMismatchError({
propertyKey: 'theme',
expectedValue: 'dark',
actualValue: 'light'
})

new HydrationMismatchError(propertyKeyOrOptions, expectedValue?, actualValue?): HydrationMismatchError

Defined in: src/core/errors.ts:605

Parameter Type
propertyKeyOrOptions string | HydrationMismatchErrorOptions
expectedValue? unknown
actualValue? unknown

HydrationMismatchError

DocumentContextError.constructor

Property Modifier Type Description Inherited from
actualValue readonly unknown - -
cause? public unknown - DocumentContextError.cause
code readonly string - DocumentContextError.code
details readonly Record<string, unknown> - DocumentContextError.details
expectedValue readonly unknown - -
message public string - DocumentContextError.message
name public string - DocumentContextError.name
propertyKey readonly string - -
resolutionGuide? readonly string - DocumentContextError.resolutionGuide
stack? public string - DocumentContextError.stack
stackTraceLimit static number The Error.stackTraceLimit property specifies the number of stack frames collected by a stack trace (whether generated by new Error().stack or Error.captureStackTrace(obj)). The default value is 10 but may be set to any valid JavaScript number. Changes will affect any stack trace captured after the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. DocumentContextError.stackTraceLimit

toString(): string

Defined in: src/core/errors.ts:55

Returns a string representation of an object.

string

DocumentContextError.toString

static captureStackTrace(targetObject, constructorOpt?): void

Defined in: node_modules/@types/node/globals.d.ts:51

Creates a .stack property on targetObject, which when accessed returns a string representing the location in the code at which Error.captureStackTrace() was called.

const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // Similar to `new Error().stack`

The first line of the trace will be prefixed with ${myObject.name}: ${myObject.message}.

The optional constructorOpt argument accepts a function. If given, all frames above constructorOpt, including constructorOpt, will be omitted from the generated stack trace.

The constructorOpt argument is useful for hiding implementation details of error generation from the user. For instance:

function a() {
b();
}
function b() {
c();
}
function c() {
// Create an error without stack trace to avoid calculating the stack trace twice.
const { stackTraceLimit } = Error;
Error.stackTraceLimit = 0;
const error = new Error();
Error.stackTraceLimit = stackTraceLimit;
// Capture the stack trace above function b
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
throw error;
}
a();
Parameter Type
targetObject object
constructorOpt? Function

void

DocumentContextError.captureStackTrace

static isError(error): error is Error

Defined in: site/node_modules/typescript/lib/lib.esnext.error.d.ts:21

Indicates whether the argument provided is a built-in Error instance or not.

Parameter Type
error unknown

error is Error

DocumentContextError.isError

static prepareStackTrace(err, stackTraces): any

Defined in: node_modules/@types/node/globals.d.ts:55

Parameter Type
err Error
stackTraces CallSite[]

any

https://v8.dev/docs/stack-trace-api#customizing-stack-traces

DocumentContextError.prepareStackTrace


Defined in: src/core/errors.ts:694

Reported when storage deserialization fails or persisted JSON is corrupt.

The storage pipeline never lets a bad payload crash mounting: synchronous read failures, async adapter rejections, BroadcastChannel message parse failures, and storage-event parse failures are all routed to the session error stream (readErrorStream) as this error, and hydration falls back to DOM/blueprint sources. Supply a migrate function in withStorage to recover old schemas instead of dropping them.

The constructor accepts either (key, rawData?) positionally or a full options bag.

keyOrOptions

Storage key or InvalidStorageDataErrorOptions.

rawData

Raw payload, used only with the string form.

import { readErrorStream } from '@sandlada/document-context'
readErrorStream(session).subscribe((err) => {
if (err.code === 'INVALID_STORAGE_DATA') {
console.warn('dropping corrupt payload for', err.details)
}
})

new InvalidStorageDataError(keyOrOptions, rawData?): InvalidStorageDataError

Defined in: src/core/errors.ts:698

Parameter Type
keyOrOptions string | InvalidStorageDataErrorOptions
rawData? unknown

InvalidStorageDataError

DocumentContextError.constructor

Property Modifier Type Description Inherited from
cause? public unknown - DocumentContextError.cause
code readonly string - DocumentContextError.code
details readonly Record<string, unknown> - DocumentContextError.details
key readonly string - -
message public string - DocumentContextError.message
name public string - DocumentContextError.name
rawData readonly unknown - -
resolutionGuide? readonly string - DocumentContextError.resolutionGuide
stack? public string - DocumentContextError.stack
stackTraceLimit static number The Error.stackTraceLimit property specifies the number of stack frames collected by a stack trace (whether generated by new Error().stack or Error.captureStackTrace(obj)). The default value is 10 but may be set to any valid JavaScript number. Changes will affect any stack trace captured after the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. DocumentContextError.stackTraceLimit

toString(): string

Defined in: src/core/errors.ts:55

Returns a string representation of an object.

string

DocumentContextError.toString

static captureStackTrace(targetObject, constructorOpt?): void

Defined in: node_modules/@types/node/globals.d.ts:51

Creates a .stack property on targetObject, which when accessed returns a string representing the location in the code at which Error.captureStackTrace() was called.

const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // Similar to `new Error().stack`

The first line of the trace will be prefixed with ${myObject.name}: ${myObject.message}.

The optional constructorOpt argument accepts a function. If given, all frames above constructorOpt, including constructorOpt, will be omitted from the generated stack trace.

The constructorOpt argument is useful for hiding implementation details of error generation from the user. For instance:

function a() {
b();
}
function b() {
c();
}
function c() {
// Create an error without stack trace to avoid calculating the stack trace twice.
const { stackTraceLimit } = Error;
Error.stackTraceLimit = 0;
const error = new Error();
Error.stackTraceLimit = stackTraceLimit;
// Capture the stack trace above function b
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
throw error;
}
a();
Parameter Type
targetObject object
constructorOpt? Function

void

DocumentContextError.captureStackTrace

static isError(error): error is Error

Defined in: site/node_modules/typescript/lib/lib.esnext.error.d.ts:21

Indicates whether the argument provided is a built-in Error instance or not.

Parameter Type
error unknown

error is Error

DocumentContextError.isError

static prepareStackTrace(err, stackTraces): any

Defined in: node_modules/@types/node/globals.d.ts:55

Parameter Type
err Error
stackTraces CallSite[]

any

https://v8.dev/docs/stack-trace-api#customizing-stack-traces

DocumentContextError.prepareStackTrace


Defined in: src/core/errors.ts:508

Thrown when a bridge path attempts prototype pollution or targets an XSS sink.

Matching is case-insensitive per dot-segment. Blocked tokens are __proto__, prototype, constructor; blocked sinks are innerHTML, outerHTML, insertAdjacentHTML, srcdoc, script, and eval. Thrown eagerly by validatePropertyPath() from both readDomProperty() and writeDomProperty(), so a bad mapping fails at mount rather than silently writing somewhere dangerous.

The constructor accepts either (dangerousPath, targetNode?) positionally or a full options bag.

dangerousPathOrOptions

Rejected path or PropertySyncSecurityErrorOptions bag.

targetNode

Host element, used only with the string form.

import { withBridge } from '@sandlada/document-context'
// Throws at mount: 'innerHTML' is a forbidden sink.
const bad = pipe(base, withBridge({ properties: { html: 'innerHTML' } }))

new PropertySyncSecurityError(dangerousPathOrOptions, targetNode?): PropertySyncSecurityError

Defined in: src/core/errors.ts:512

Parameter Type
dangerousPathOrOptions string | PropertySyncSecurityErrorOptions
targetNode? string | HTMLElement

PropertySyncSecurityError

DocumentContextError.constructor

Property Modifier Type Description Inherited from
cause? public unknown - DocumentContextError.cause
code readonly string - DocumentContextError.code
dangerousPath readonly string - -
details readonly Record<string, unknown> - DocumentContextError.details
message public string - DocumentContextError.message
name public string - DocumentContextError.name
resolutionGuide? readonly string - DocumentContextError.resolutionGuide
stack? public string - DocumentContextError.stack
targetNode? readonly string | HTMLElement - -
stackTraceLimit static number The Error.stackTraceLimit property specifies the number of stack frames collected by a stack trace (whether generated by new Error().stack or Error.captureStackTrace(obj)). The default value is 10 but may be set to any valid JavaScript number. Changes will affect any stack trace captured after the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. DocumentContextError.stackTraceLimit

toString(): string

Defined in: src/core/errors.ts:55

Returns a string representation of an object.

string

DocumentContextError.toString

static captureStackTrace(targetObject, constructorOpt?): void

Defined in: node_modules/@types/node/globals.d.ts:51

Creates a .stack property on targetObject, which when accessed returns a string representing the location in the code at which Error.captureStackTrace() was called.

const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // Similar to `new Error().stack`

The first line of the trace will be prefixed with ${myObject.name}: ${myObject.message}.

The optional constructorOpt argument accepts a function. If given, all frames above constructorOpt, including constructorOpt, will be omitted from the generated stack trace.

The constructorOpt argument is useful for hiding implementation details of error generation from the user. For instance:

function a() {
b();
}
function b() {
c();
}
function c() {
// Create an error without stack trace to avoid calculating the stack trace twice.
const { stackTraceLimit } = Error;
Error.stackTraceLimit = 0;
const error = new Error();
Error.stackTraceLimit = stackTraceLimit;
// Capture the stack trace above function b
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
throw error;
}
a();
Parameter Type
targetObject object
constructorOpt? Function

void

DocumentContextError.captureStackTrace

static isError(error): error is Error

Defined in: site/node_modules/typescript/lib/lib.esnext.error.d.ts:21

Indicates whether the argument provided is a built-in Error instance or not.

Parameter Type
error unknown

error is Error

DocumentContextError.isError

static prepareStackTrace(err, stackTraces): any

Defined in: node_modules/@types/node/globals.d.ts:55

Parameter Type
err Error
stackTraces CallSite[]

any

https://v8.dev/docs/stack-trace-api#customizing-stack-traces

DocumentContextError.prepareStackTrace


Defined in: src/core/errors.ts:419

Thrown when inject() is called on a detached DOM node.

Scope resolution depends on context-request event bubbling through connected ancestors, so node.isConnected must be true. The single exception is a token already present in the global singleton registry, which resolves without DOM traversal. Otherwise, defer injection to connectedCallback() or to after parent.appendChild(node).

The constructor accepts either (token, targetNode?) positionally or a full options bag.

tokenOrOptions

Token name or UnconnectedNodeErrorOptions.

targetNode

Detached node, used only with the string form.

import { inject } from '@sandlada/document-context'
class MyEl extends HTMLElement {
connectedCallback() {
const svc = inject('logger')(this)
}
}

new UnconnectedNodeError(tokenOrOptions, targetNode?): UnconnectedNodeError

Defined in: src/core/errors.ts:423

Parameter Type
tokenOrOptions string | UnconnectedNodeErrorOptions
targetNode? HTMLElement | Node

UnconnectedNodeError

DocumentContextError.constructor

Property Modifier Type Description Inherited from
cause? public unknown - DocumentContextError.cause
code readonly string - DocumentContextError.code
details readonly Record<string, unknown> - DocumentContextError.details
message public string - DocumentContextError.message
name public string - DocumentContextError.name
resolutionGuide? readonly string - DocumentContextError.resolutionGuide
stack? public string - DocumentContextError.stack
targetNode? readonly HTMLElement | Node - -
token readonly string - -
stackTraceLimit static number The Error.stackTraceLimit property specifies the number of stack frames collected by a stack trace (whether generated by new Error().stack or Error.captureStackTrace(obj)). The default value is 10 but may be set to any valid JavaScript number. Changes will affect any stack trace captured after the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. DocumentContextError.stackTraceLimit

toString(): string

Defined in: src/core/errors.ts:55

Returns a string representation of an object.

string

DocumentContextError.toString

static captureStackTrace(targetObject, constructorOpt?): void

Defined in: node_modules/@types/node/globals.d.ts:51

Creates a .stack property on targetObject, which when accessed returns a string representing the location in the code at which Error.captureStackTrace() was called.

const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // Similar to `new Error().stack`

The first line of the trace will be prefixed with ${myObject.name}: ${myObject.message}.

The optional constructorOpt argument accepts a function. If given, all frames above constructorOpt, including constructorOpt, will be omitted from the generated stack trace.

The constructorOpt argument is useful for hiding implementation details of error generation from the user. For instance:

function a() {
b();
}
function b() {
c();
}
function c() {
// Create an error without stack trace to avoid calculating the stack trace twice.
const { stackTraceLimit } = Error;
Error.stackTraceLimit = 0;
const error = new Error();
Error.stackTraceLimit = stackTraceLimit;
// Capture the stack trace above function b
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
throw error;
}
a();
Parameter Type
targetObject object
constructorOpt? Function

void

DocumentContextError.captureStackTrace

static isError(error): error is Error

Defined in: site/node_modules/typescript/lib/lib.esnext.error.d.ts:21

Indicates whether the argument provided is a built-in Error instance or not.

Parameter Type
error unknown

error is Error

DocumentContextError.isError

static prepareStackTrace(err, stackTraces): any

Defined in: node_modules/@types/node/globals.d.ts:55

Parameter Type
err Error
stackTraces CallSite[]

any

https://v8.dev/docs/stack-trace-api#customizing-stack-traces

DocumentContextError.prepareStackTrace


Defined in: src/core/errors.ts:191

Thrown when inject() finds no provider for a token in the DOM scope chain.

Resolution walks the context-request event path up to the document root and then consults the global singleton registry. If every step misses, this error is thrown. Register the token with withProvider() / withAsyncProvider() on an ancestor blueprint, or seed it via setGlobalSingleton().

The constructor accepts either (token, targetElement?) positionally or a full options bag.

tokenOrOptions

Token name string or UnknownServiceErrorOptions bag.

targetElement

Host element, used only with the string form.

import { inject, withProvider, createContext, pipe, mount } from '@sandlada/document-context'
try {
inject('auth-service')(document.getElementById('login')!)
} catch (err) {
console.error(String(err))
}

new UnknownServiceError(tokenOrOptions, targetElement?): UnknownServiceError

Defined in: src/core/errors.ts:195

Parameter Type
tokenOrOptions string | UnknownServiceErrorOptions
targetElement? string | HTMLElement

UnknownServiceError

DocumentContextError.constructor

Property Modifier Type Description Inherited from
cause? public unknown - DocumentContextError.cause
code readonly string - DocumentContextError.code
details readonly Record<string, unknown> - DocumentContextError.details
message public string - DocumentContextError.message
name public string - DocumentContextError.name
resolutionGuide? readonly string - DocumentContextError.resolutionGuide
stack? public string - DocumentContextError.stack
targetElement? readonly string | HTMLElement - -
token readonly string - -
stackTraceLimit static number The Error.stackTraceLimit property specifies the number of stack frames collected by a stack trace (whether generated by new Error().stack or Error.captureStackTrace(obj)). The default value is 10 but may be set to any valid JavaScript number. Changes will affect any stack trace captured after the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. DocumentContextError.stackTraceLimit

toString(): string

Defined in: src/core/errors.ts:55

Returns a string representation of an object.

string

DocumentContextError.toString

static captureStackTrace(targetObject, constructorOpt?): void

Defined in: node_modules/@types/node/globals.d.ts:51

Creates a .stack property on targetObject, which when accessed returns a string representing the location in the code at which Error.captureStackTrace() was called.

const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // Similar to `new Error().stack`

The first line of the trace will be prefixed with ${myObject.name}: ${myObject.message}.

The optional constructorOpt argument accepts a function. If given, all frames above constructorOpt, including constructorOpt, will be omitted from the generated stack trace.

The constructorOpt argument is useful for hiding implementation details of error generation from the user. For instance:

function a() {
b();
}
function b() {
c();
}
function c() {
// Create an error without stack trace to avoid calculating the stack trace twice.
const { stackTraceLimit } = Error;
Error.stackTraceLimit = 0;
const error = new Error();
Error.stackTraceLimit = stackTraceLimit;
// Capture the stack trace above function b
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
throw error;
}
a();
Parameter Type
targetObject object
constructorOpt? Function

void

DocumentContextError.captureStackTrace

static isError(error): error is Error

Defined in: site/node_modules/typescript/lib/lib.esnext.error.d.ts:21

Indicates whether the argument provided is a built-in Error instance or not.

Parameter Type
error unknown

error is Error

DocumentContextError.isError

static prepareStackTrace(err, stackTraces): any

Defined in: node_modules/@types/node/globals.d.ts:55

Parameter Type
err Error
stackTraces CallSite[]

any

https://v8.dev/docs/stack-trace-api#customizing-stack-traces

DocumentContextError.prepareStackTrace

Defined in: src/core/errors.ts:242

Options bag for AsyncServiceNotReadyError.

Property Modifier Type Description
cause? readonly unknown Underlying cause.
details? readonly Record<string, unknown> Extra diagnostics merged with { token }.
message? readonly string Custom message override.
resolutionGuide? readonly string Fix hint override.
token readonly string Requested async token name.

Defined in: src/core/errors.ts:75

Options bag for CircularDependencyError.

Property Modifier Type Description
cause? readonly unknown Underlying cause.
dependencyPath readonly readonly string[] Ordered token-name cycle, for example ['a', 'b', 'a'].
details? readonly Record<string, unknown> Extra diagnostics merged with { dependencyPath }.
message? readonly string Custom message override. Defaults to `Circular dependency detected: ${path.join(' -> ')}`.
resolutionGuide? readonly string Fix hint override.

Defined in: src/core/errors.ts:309

Options bag for DisposedSessionError.

Property Modifier Type Description
cause? readonly unknown Underlying cause.
details? readonly Record<string, unknown> Extra diagnostics.
message? readonly string Custom message override.
resolutionGuide? readonly string Fix hint override.
sessionKey? readonly string Value of data-context-key when known. Used only for diagnostics.

Defined in: src/core/errors.ts:11

Options bag for constructing a DocumentContextError.

Property Modifier Type Description
cause? readonly unknown Underlying cause, forwarded to Error options.
code readonly string Stable machine-readable code (for example 'UNKNOWN_SERVICE'). Surfaced as error.code and in toString().
details? readonly Record<string, unknown> Structured diagnostic payload, always defaulted to {}.
message readonly string Human-readable message.
resolutionGuide? readonly string Actionable fix hint appended to toString().

Defined in: src/core/errors.ts:561

Options bag for HydrationMismatchError.

Property Modifier Type Description
actualValue readonly unknown Value actually applied after the merge.
cause? readonly unknown Underlying cause.
details? readonly Record<string, unknown> Extra diagnostics.
expectedValue readonly unknown Value from the reference source (per strategy).
message? readonly string Custom message override.
propertyKey readonly string State key whose sources disagreed.
resolutionGuide? readonly string Fix hint override.

Defined in: src/core/types.ts:276

Asynchronous key-value persistence adapter.

Same shape as IStorageAdapter but every method returns a Promise. Reads hydrate late (applied via update() after mount); writes report failures to the session error stream instead of throwing.

const idbAdapter = {
getItem: async (key: string) => (await idb.get(key)) ?? null,
setItem: async (key: string, value: string) => { await idb.set(key, value) },
removeItem: async (key: string) => { await idb.del(key) }
}
Type Parameter Default type
S any

getItem(key): Promise<string | S | null>

Defined in: src/core/types.ts:277

Parameter Type
key string

Promise<string | S | null>

removeItem(key): Promise<void>

Defined in: src/core/types.ts:279

Parameter Type
key string

Promise<void>

setItem(key, value): Promise<void>

Defined in: src/core/types.ts:278

Parameter Type
key string
value string | S

Promise<void>


Defined in: src/core/types.ts:202

Bidirectional state-DOM synchronization options for withBridge.

Each withBridge() call appends one IBridgeOptions entry to blueprint.bridges and registers a mount hook that activates it. Multiple entries accumulate; later entries do not replace earlier ones.

import { withBridge } from '@sandlada/document-context'
const blueprint = pipe(
createContext({ query: '' }),
withBridge({
properties: { query: 'value' },
events: ['input'],
batch: true
})
)
Type Parameter Default type
S any
Property Modifier Type Description
activeElementGuard? readonly boolean Declared focus guard flag. Currently reserved: cursor preservation for text inputs is always applied via safeWriteValueWithCursor regardless of this value.
batch? readonly boolean When not false (default), state-to-DOM writes are coalesced into a single queueMicrotask flush per tick. Set to false for synchronous writes.
conflict? readonly "lastWriteWins" | "statePrecedence" | "domPrecedence" Declared conflict policy for simultaneous writes. Currently reserved: the runtime applies last-write-wins ordering and does not yet branch on this value.
events? readonly readonly string[] DOM event names that trigger DOM-to-state sync. Defaults to ['input', 'change'].
properties readonly { readonly [K in string | number | symbol]?: string | IBridgePropertyRule<S> } Map from state key to DOM path or IBridgePropertyRule. Only listed keys are synchronized; unlisted state stays memory-only.

Defined in: src/core/types.ts:159

Binding rule mapping one state key to one DOM property path.

The shorthand form is a bare path string (for example 'dataset.count'). The object form adds direction-specific codecs plus the DOM event that triggers DOM-to-state sync for this binding.

import { withBridge, parseNumber } from '@sandlada/document-context'
const blueprint = pipe(
createContext({ count: 0, theme: 'light' }),
withBridge({
properties: {
count: { target: 'dataset.count', parse: parseNumber },
theme: 'aria-theme'
}
})
)
Type Parameter Default type
S any
Property Modifier Type Description
event? readonly string DOM event name that triggers a DOM-to-state read for this binding. Falls back to the bridge-level events option (['input', 'change'] by default) when omitted.
parse? readonly (domValue) => any Codec for the DOM-to-state direction: converts the raw DOM value (always a string for dataset/attribute/style paths) back to the state type. Omit for identity.
target readonly string DOM property path. Supported grammars: dataset.*, style.*, style.--*, aria-*, value, `checked
transform? readonly (stateValue) => string | number | boolean | null Codec for the state-to-DOM direction: converts the state value to a DOM-writable `string

Defined in: src/core/types.ts:426

Pure, immutable blueprint: the Phase 1 declaration of state shape, service providers, DOM bridges, persistence, and lifecycle hooks.

Blueprints never touch the DOM, perform no I/O, and install no listeners. Operators (withProvider, withBridge, withStorage, withHook) return a new blueprint with structural sharing; mount(blueprint)(element) is the single execution boundary that turns a blueprint into a live ISession.

import { createContext, pipe, withProvider } from '@sandlada/document-context'
const blueprint = pipe(
createContext({ count: 0 }),
withProvider('logger', () => new ConsoleLogger())
)
Type Parameter Default type
S extends Record<PropertyKey, any> Record<PropertyKey, any>
Services object
Property Modifier Type Description
bridges readonly readonly IBridgeOptions<S>[] Accumulated bridge option entries, applied in order at mount time.
hooks readonly readonly ILifecycleHookRegistration<S, Services>[] Accumulated lifecycle hook registrations.
initialState readonly Readonly<S> Seed state snapshot. Frozen in development builds.
providers readonly ReadonlyMap<string | ServiceToken<any, string>, IServiceRegistration<any>> Service registrations keyed by string or ServiceToken.
storage? readonly Readonly<IStorageOptions<S>> Persistence options when withStorage was applied.

Defined in: src/core/session-internal.ts:25

Mutable runtime internals for one live session, held in a WeakMap keyed by the opaque ISession handle (never exposed publicly).

Type Parameter Default type
S extends Record<PropertyKey, any> Record<PropertyKey, any>
Property Type Description
abortController AbortController Aborted on dispose(); its signal is surfaced as session.abortSignal.
cleanups () => void | Promise<void>[] Mount/plugin/hook cleanups executed LIFO at dispose.
dirtyQueue (state) => S | Partial<S>[] Updaters accumulated while suspended, replayed on resuscitation.
errorSubject ReplaySubject<DocumentContextError> ReplaySubject(20) collecting non-fatal runtime errors; observed via readErrorStream().
inFlightAsyncServices Map<string | ServiceToken<any, string>, Promise<any>> Coalesced in-flight async factories.
isDisposed boolean Permanent terminal flag.
isSuspended boolean Parked-in-TTL flag for keyed sessions.
scopedServices Map<string | ServiceToken<any, string>, any> Cache for 'scoped' lifecycle instances.
sessionKey? string Value of data-context-key, if present.
stateSubject BehaviorSubject<S> BehaviorSubject holding the current immutable state; seeded from blueprint.initialState (post-hydration).
target HTMLElement Current host element (repointed on resuscitation).

Defined in: src/core/types.ts:391

Stored lifecycle hook registration inside a blueprint.

Type Parameter Default type
S extends Record<PropertyKey, any> Record<PropertyKey, any>
Services object
Property Modifier Type Description
event readonly keyof ILifecycleHooks<Record<PropertyKey, any>, { }> Event channel the handler subscribes to.
handler readonly Function Untyped handler reference; invocation is typed by the mount / dispose / observer pipelines according to event.

Defined in: src/core/types.ts:367

Lifecycle hook callbacks keyed by event name.

Register entries with withHook(event, handler); all five channels are optional and may hold at most one handler shape each per call (multiple withHook calls for the same event accumulate in FIFO mount order).

import { withHook } from '@sandlada/document-context'
const blueprint = pipe(
base,
withHook('mount', (session) => {
console.log('mounted on', session.target)
return () => console.log('cleaned up')
}),
withHook('dispose', (session) => console.log('disposed'))
)
Type Parameter Default type
S extends Record<PropertyKey, any> Record<PropertyKey, any>
Services object
Property Modifier Type Description
adopt? readonly (session, newDocument) => void Runs when a session migrates to a new Document (for example an iframe) via adoptSessionToDocument. Receives (session, newDocument).
dispose? readonly (session) => void | Promise<void> Runs during dispose() in LIFO order, before mount cleanups. Fire-and-forget async is tolerated; rejections are routed to the session error stream.
mount? readonly (session) => HookCleanup | Promise<HookCleanup> Runs after the session is cached and global mount plugins have executed. May return a cleanup pushed onto the dispose stack.
resuscitate? readonly (session, newTarget) => void Runs when a suspended keyed session is reattached to a new host via resuscitateKeyedSession. Receives (session, newTarget).
suspend? readonly (session) => void Runs when a keyed (data-context-key) host disconnects and enters the 50ms suspended window instead of being disposed immediately.

Defined in: src/core/errors.ts:657

Options bag for InvalidStorageDataError.

Property Modifier Type Description
cause? readonly unknown Underlying cause.
details? readonly Record<string, unknown> Extra diagnostics (usually { error: String(err) }).
key readonly string Storage key that failed to deserialize.
message? readonly string Custom message override.
rawData readonly unknown Raw payload (or the caught exception when deserialization itself threw). Kept as-is for inspection; may be null.
resolutionGuide? readonly string Fix hint override.

Defined in: src/core/types.ts:112

Immutable registration record for one service provider.

This is a pure data structure held inside IContextBlueprint.providers. It is created by withProvider / withAsyncProvider and consumed by the inject / injectAsync resolution pipeline. Never constructed by hand in application code.

Type Parameter Default type
T unknown
Property Modifier Type Description
factory readonly (session) => T | Promise<T> Instantiation function invoked lazily on first injection (or eagerly for async singletons under mountAsync). Receives the owning session so the factory can itself call inject / select.
isAsync readonly boolean true when the factory returns a Promise. Async services reject synchronous inject() with AsyncServiceNotReadyError and must be resolved via injectAsync().
lifecycle readonly ServiceLifecycle Caching policy; see ServiceLifecycle.
multi? readonly boolean Reserved multi-provider flag. When true, the registration participates in injectAll() accumulation instead of first-match short-circuiting.
token readonly string | ServiceToken<T, string> Lookup key: either a plain string or a branded ServiceToken. String tokens collide by text; token objects collide by object identity.

Defined in: src/core/types.ts:469

Opaque handle to a mounted, live runtime session (Phase 2).

Obtained only from mount(blueprint)(element) or mountAsync. Carries the host element, the originating blueprint, a disposal flag, and an AbortSignal bound to the session lifetime. State itself is never stored on this object; it lives in the internal BehaviorSubject keyed by WeakMap.

import { mount, select, update } from '@sandlada/document-context'
const session = mount(blueprint)(document.getElementById('counter')!)
const getCount = select((s) => s.count)
console.log(getCount(session))
// ExplicitResourceManagement (optional)
{
using s = mount(blueprint)(document.getElementById('x')!)
}
Type Parameter Default type
S extends Record<PropertyKey, any> Record<PropertyKey, any>
Services object
Property Modifier Type Description
[asyncDispose] readonly () => Promise<void> -
[dispose] readonly () => void -
[SessionBrand] readonly true -
abortSignal readonly AbortSignal Aborts when the session is disposed; pass to fetch or async providers for cooperative cancellation.
blueprint readonly IContextBlueprint<S, Services> Originating immutable blueprint.
isDisposed readonly boolean true after dispose(); subsequent update() calls silently no-op and return false.
target readonly HTMLElement Host element the blueprint was mounted on.

Defined in: src/core/types.ts:254

Synchronous key-value persistence adapter.

Any object with the getItem / setItem / removeItem shape qualifies, so the built-in localStorage / sessionStorage adapters, a custom in-memory map, or an IndexedDB-backed synchronous facade can be supplied to withStorage.

const memory = new Map<string, string>()
const adapter = {
getItem: (key: string) => memory.get(key) ?? null,
setItem: (key: string, value: string) => { memory.set(key, String(value)) },
removeItem: (key: string) => { memory.delete(key) }
}
Type Parameter Default type
S any

getItem(key): string | S | null

Defined in: src/core/types.ts:255

Parameter Type
key string

string | S | null

removeItem(key): void

Defined in: src/core/types.ts:257

Parameter Type
key string

void

setItem(key, value): void

Defined in: src/core/types.ts:256

Parameter Type
key string
value string | S

void


Defined in: src/core/types.ts:315

State persistence and rehydration options for withStorage.

import { withStorage } from '@sandlada/document-context'
const blueprint = pipe(
createContext({ count: 0 }),
withStorage({
adapter: 'localStorage',
key: 'counter-app',
version: 2,
migrate: (old, oldVersion) => ({ count: Number((old as any).count) || 0 })
})
)
Type Parameter Default type
S any
Property Modifier Type Description
adapter readonly IStorageAdapter<S> | IAsyncStorageAdapter<S> | "localStorage" | "sessionStorage" 'localStorage', 'sessionStorage', or a custom sync/async adapter object. String shorthands resolve to Web Storage with an in-memory fallback outside the browser.
crossTabSync? readonly boolean When not false (default enabled), state changes broadcast via BroadcastChannel and incoming storage events are applied.
hydrationStrategy? readonly HydrationStrategy Initial merge precedence; see HydrationStrategy. Defaults to 'storageFirst'.
key readonly string Storage key (and BroadcastChannel / Web Locks namespace suffix). Unique per persisted blueprint.
migrate? readonly (persistedState, oldVersion) => Partial<S> Migration invoked when the persisted __v is older than version. Receives (persistedState, oldVersion) and returns the partial state to hydrate.
version? readonly number Schema version stamped into the {"__v", "data"} envelope. Defaults to 1.

Defined in: src/core/async-provider.ts:19

Options for withAsyncProvider.

Property Modifier Type Description
lifecycle? readonly ServiceLifecycle Caching policy, defaulting to 'scoped'. Resolved async values are cached the same way as sync ones ('singleton' lands in the global registry, 'scoped' on the session, 'transient' is never cached).
multi? readonly boolean When true, the registration participates in injectAllAsync() accumulation. Defaults to false.

Defined in: src/core/providers.ts:19

Options for withProvider / withAsyncProvider.

Property Modifier Type Description
lifecycle? readonly ServiceLifecycle Caching policy, defaulting to 'scoped'. See ServiceLifecycle: 'singleton' shares one instance page-wide, 'scoped' caches one instance per session, 'transient' creates a fresh instance per injection.
multi? readonly boolean When true, the registration participates in injectAll() accumulation. Defaults to false (first-match wins).

Defined in: src/core/errors.ts:472

Options bag for PropertySyncSecurityError.

Property Modifier Type Description
cause? readonly unknown Underlying cause.
dangerousPath readonly string Rejected bridge path as written in the blueprint.
details? readonly Record<string, unknown> Extra diagnostics (includes blockedSegment or blockedSink).
message? readonly string Custom message override.
resolutionGuide? readonly string Fix hint override.
targetNode? readonly string | HTMLElement Host element or tag-name string for diagnostics.

Defined in: src/core/types.ts:502

Global service-type registry for string tokens.

Augment this interface once per application (declaration merging) so that inject('auth-service') returns a typed value instead of unknown.

declare module '@sandlada/document-context' {
interface ServiceRegistry {
'auth-service': AuthService
'theme-mode': 'light' | 'dark'
}
}
import { inject } from '@sandlada/document-context'
const auth = inject('auth-service')(document.getElementById('login')!)

Defined in: src/core/types.ts:36

Branded, type-safe dependency-injection token.

A ServiceToken carries its value type T and its lookup name Name at the type level, so inject(token) can return T without a cast. Prefer tokens over raw strings when a service is shared across bundles or teams, because two strings with the same text collide while two tokens with different generic arguments do not type-check as interchangeable.

Tokens are created once with createToken and then used as the token argument of withProvider, withAsyncProvider, inject, and injectAsync.

import { createToken, withProvider, mount, inject } from '@sandlada/document-context'
import type { ServiceToken } from '@sandlada/document-context'
const LoggerToken: ServiceToken<ConsoleLogger, 'logger'> = createToken<ConsoleLogger>('logger')
const blueprint = pipe(
createContext({ count: 0 }),
withProvider(LoggerToken, () => new ConsoleLogger())
)
const session = mount(blueprint)(document.getElementById('app')!)
const logger = inject(LoggerToken)(session)
Type Parameter Default type
T -
Name extends string string
Property Modifier Type Description
__type? readonly T Phantom value-type carrier. Never assigned at runtime; exists only so TypeScript can infer T from the token.
[ServiceTokenBrand] readonly true -
name readonly Name Canonical lookup key. Used as the runtime Map key identity via object reference, and as the human-readable name in error messages.

Defined in: src/core/errors.ts:383

Options bag for UnconnectedNodeError.

Property Modifier Type Description
cause? readonly unknown Underlying cause.
details? readonly Record<string, unknown> Extra diagnostics.
message? readonly string Custom message override.
resolutionGuide? readonly string Fix hint override.
targetNode? readonly HTMLElement | Node Detached DOM node. Used only for diagnostics.
token readonly string Requested token name.

Defined in: src/core/errors.ts:154

Options bag for UnknownServiceError.

Property Modifier Type Description
cause? readonly unknown Underlying cause.
details? readonly Record<string, unknown> Extra diagnostics merged with { token }.
message? readonly string Custom message override.
resolutionGuide? readonly string Fix hint override.
targetElement? readonly string | HTMLElement Host element or tag-name string where resolution was attempted. Used only for diagnostics.
token readonly string Requested token name.

BlueprintOperator<S, InServices, OutServices> = (blueprint) => IContextBlueprint<S, OutServices>

Defined in: src/core/pipe.ts:19

Operator that transforms one blueprint into another while preserving the state shape S and threading the service-type accumulator (InServices to OutServices).

Every with* operator returns this shape, so pipe can chain them with full type inference. Operators must stay pure: clone-and-return, never mutate the input blueprint.

Type Parameter
S extends Record<PropertyKey, any>
InServices
OutServices
Parameter Type
blueprint IContextBlueprint<S, InServices>

IContextBlueprint<S, OutServices>

import { withProvider } from '@sandlada/document-context'
const addLogger = withProvider('logger', () => new ConsoleLogger())

HookCleanup = void | (() => void)

Defined in: src/core/types.ts:331

Cleanup contract for lifecycle hooks.

A hook returns either nothing or a zero-argument cleanup invoked during dispose() in LIFO order. Async cleanups are awaited only opportunistically; prefer synchronous cleanup.


HydrationStrategy = "storageFirst" | "domFirst" | "blueprintFirst" | "merge"

Defined in: src/core/types.ts:235

Precedence policy for the initial hydration merge in resolveHydratedState.

The three sources are always blueprint initialState, live DOM properties (extracted via bridge paths), and persisted storage data. The strategy only controls spread order, later sources winning:

  • 'storageFirst' (default) — {...blueprint, ...dom, ...storage}.
  • 'domFirst'{...blueprint, ...storage, ...dom}. Note: the current implementation treats 'merge' identically to 'domFirst'.
  • 'blueprintFirst'{...dom, ...storage, ...blueprint}.
  • 'merge' — currently an alias of 'domFirst'.
import { withStorage } from '@sandlada/document-context'
const blueprint = pipe(
createContext({ count: 0 }),
withStorage({ adapter: 'localStorage', key: 'counter', hydrationStrategy: 'domFirst' })
)

LifecycleEventName = keyof ILifecycleHooks

Defined in: src/core/types.ts:382

Valid lifecycle event names. Equal to keyof ILifecycleHooks: 'mount' | 'dispose' | 'suspend' | 'resuscitate' | 'adopt'.


MountPlugin = (session, internal) => void | (() => void) | Promise<void | (() => void)>

Defined in: src/core/mount.ts:25

Runtime plugin invoked once per mount() call, after the session handle is cached but before blueprint mount hooks run.

A plugin may return a zero-argument cleanup (synchronous or promise-like) that is pushed onto the session dispose stack and executed LIFO at dispose() time. The built-in bridge, storage, and DOM-responder plugins are all registered through this mechanism.

Parameter Type Description
session ISession<any, any> Freshly created live session.
internal IInternalSessionState<any> Internal mutable state (subjects, caches, flags).

void | (() => void) | Promise<void | (() => void)>

An optional cleanup, or a promise of one.


ServiceLifecycle = "singleton" | "scoped" | "transient"

Defined in: src/core/types.ts:88

Lifetime policy for a registered service.

  • 'singleton' — one instance per page, stored in the global singleton registry and shared across every session and DOM scope.
  • 'scoped' — one instance per mounted session, cached on the session internals and shared by every injection within that session scope.
  • 'transient' — a fresh instance on every inject() call; never cached.
import { withProvider } from '@sandlada/document-context'
const blueprint = pipe(
base,
withProvider('config', () => loadConfig(), { lifecycle: 'singleton' }),
withProvider('form-state', () => createFormState(), { lifecycle: 'scoped' }),
withProvider('id', () => crypto.randomUUID(), { lifecycle: 'transient' })
)

Creates a pure, immutable blueprint seed for a context container (Phase 1).

This is the entry point of the two-phase architecture. It performs zero DOM access, zero I/O, and installs zero listeners: it only snapshots initialState and returns an empty-provider blueprint that later operators (withProvider, withBridge, withStorage, withHook) extend via pipe. The single execution boundary is mount(blueprint)(element).

In development (NODE_ENV !== 'production') the snapshot is shallow-frozen with Object.freeze({ ...initialState }) so accidental mutation throws early; in production the reference is kept as-is for speed.

export function createContext<S extends Record<PropertyKey, any>>(
initialState: S
): IContextBlueprint<S, {}>;

Defined in: src/core/context.ts

import { createContext, pipe, withProvider, mount } from '@sandlada/document-context'
const blueprint = pipe(
createContext({ count: 0, theme: 'light' as 'light' | 'dark' }),
withProvider('logger', () => new ConsoleLogger())
)
const session = mount(blueprint)(document.getElementById('app')!)

Allocates fresh internals for a mount: seeds the state subject from the blueprint, creates the error replay subject, abort controller, and empty caches, and captures the data-context-key session key when present.


export function createInternalSessionState<S extends Record<PropertyKey, any>>(
target: HTMLElement,
blueprint: IContextBlueprint<S, any>
): IInternalSessionState<S>;

Defined in: src/core/session-internal.ts

Creates a branded ServiceToken for type-safe dependency injection.

This is a pure factory: it allocates a fresh token object, performs no I/O, and registers nothing. Each call returns a distinct object identity, so two tokens created with the same name string are still different runtime keys. Create tokens once at module scope and reuse them.

export function createToken<T, Name extends string = string>(name: Name): ServiceToken<T, Name>;

Defined in: src/core/types.ts

import { createToken } from '@sandlada/document-context'
const AuthToken = createToken<AuthService>('auth-service')
const ThemeToken = createToken<'light' | 'dark'>('theme-mode')

Tears down a mounted session and releases every resource it owns.

Idempotent: disposing twice (or disposing a session with no internals) is a safe no-op. The pipeline is: mark isDisposed and drop the element mapping → abortController.abort() (cancelling session.abortSignal consumers) → run blueprint dispose hooks LIFO → run mount/plugin cleanups LIFO → dispatch a bubbling, composed context-dispose CustomEvent with detail: { sessionKey }. Synchronous throws and async rejections inside hooks or cleanups are caught and routed to the session error stream as DISPOSE_HOOK_ERROR / MOUNT_CLEANUP_ERROR instead of propagating.

After disposal, update() returns false, subscribe() returns an empty unsubscribe, and session.isDisposed stays true permanently.

export function dispose<S extends Record<PropertyKey, any>, Services>(
session: ISession<S, Services>
): void;

Defined in: src/core/dispose.ts

import { dispose, update } from '@sandlada/document-context'
dispose(session)
console.log(session.isDisposed)
update({ count: 1 })(session) // false, silent no-op

Looks up the live session parked under a data-context-key.

Returns the registered session even while it is suspended (that is the resuscitation window). Callers must still check session.isDisposed, because expired or explicitly disposed sessions stay mapped until the TTL timer or unregisterKeyedSession() clears them.

export function findKeyedSession<S extends Record<PropertyKey, any> = Record<PropertyKey, any>>(
key: string
): ISession<S, any> | undefined;

Defined in: src/core/resuscitation.ts

import { findKeyedSession } from '@sandlada/document-context'
const existing = findKeyedSession('cart-panel')
if (existing && !existing.isDisposed) {
console.log('reusing suspended session')
}

Returns the live session cached for a host element, if any.


export function getElementSession<S extends Record<PropertyKey, any> = Record<PropertyKey, any>>(
element: HTMLElement
): ISession<S, any> | undefined;

Defined in: src/core/session-internal.ts

Returns the internals for a session handle, or undefined when the session was never mounted through this runtime (or its entry was collected).


export function getInternalSession<S extends Record<PropertyKey, any> = Record<PropertyKey, any>>(
session: ISession<S, any>
): IInternalSessionState<S> | undefined;

Defined in: src/core/session-internal.ts

Property / accessor decorator that lazily resolves a service on each access.

Supports two decorator protocols: legacy TypeScript experimental decorators ((target, propertyKey, descriptor?)) and TC39 Stage 3 decorators (kind === 'field' | 'getter' | 'method' | 'accessor'). In both cases the installed getter resolves inject(token)(host) per access, where host is this.element ?? this.target ?? this, so host-backed components (custom elements, controllers) resolve against their own DOM scope. Resolution is lazy and uncached: every property read re-runs injection.

This is the class-body counterpart of function-style dom/inject, which takes an explicit (elementOrSession) target instead of this.

export function injectDecorator<K extends string | ServiceToken<any>>(token: K);

Defined in: src/core/decorators.ts

import { injectDecorator } from '@sandlada/document-context'
class MyPanel {
element!: HTMLElement
@injectDecorator('logger')
declare logger: ConsoleLogger
}

Reports whether the current build is a development build.

Returns false only when globalThis.process.env.NODE_ENV is exactly 'production'; every other environment (including browsers without process) counts as development and enables freezing and extra checks. Never throws; sandbox failures also yield false.


export function isDev(): boolean;

Defined in: src/core/session-internal.ts

Mounts a pure blueprint onto a physical host element (Phase 2 execution boundary), activating the runtime session.

Idempotent per element: mounting the same HTMLElement twice returns the existing non-disposed session instead of creating a second one. On a fresh mount the pipeline is: create internal state (BehaviorSubject seeded from blueprint.initialState) → cache session in both WeakMaps → run global mount plugins in order → run blueprint mount hooks FIFO (promise cleanups are attached when they settle) → dispatch a bubbling, composed context-mount CustomEvent with detail: { session }.

Plugin and hook throws never propagate; they are routed to the session error stream as MOUNT_PLUGIN_ERROR / MOUNT_HOOK_ERROR.

export function mount<S extends Record<PropertyKey, any>, Services>(
blueprint: IContextBlueprint<S, Services>
): (element: HTMLElement) => ISession<S, Services>;

Defined in: src/core/mount.ts

import { createContext, mount, select } from '@sandlada/document-context'
const blueprint = createContext({ count: 0 })
const mountCounter = mount(blueprint)
const session = mountCounter(document.getElementById('counter')!)
const getCount = select((s: { count: number }) => s.count)
console.log(getCount(session))

Asynchronous mount boundary: mounts synchronously, then yields one microtask before resolving.

This is intentionally thin: it delegates to mount(blueprint)(element) and awaits Promise.resolve(), giving pending mount-hook microtasks and async storage hydration callbacks a chance to run. It does not itself await async providers to completion; resolve those explicitly with injectAsync(). Mounting the same element twice is idempotent and returns the cached session, same as mount.

export function mountAsync<S extends Record<PropertyKey, any>, Services>(
blueprint: IContextBlueprint<S, Services>
): (element: HTMLElement) => Promise<ISession<S, Services>>;

Defined in: src/core/mount-async.ts

import { mountAsync, injectAsync } from '@sandlada/document-context'
const session = await mountAsync(blueprint)(document.getElementById('app')!)
const config = await injectAsync('remote-config')(session)

Composes blueprint operators left-to-right into a single immutable blueprint.

Pure Phase 1 composition: no DOM access, no I/O, no listeners. Each operator receives the blueprint returned by the previous one, so service types accumulate (S0 through S10) with full inference. Calling pipe(source) with no operators returns source unchanged.

Overloads cover chains of up to ten operators with precise typing; longer chains fall through to the variadic implementation signature.

export function pipe<S extends Record<PropertyKey, any>, S0>(
source: IContextBlueprint<S, S0>
): IContextBlueprint<S, S0>
export function pipe<S extends Record<PropertyKey, any>, S0, S1>(
source: IContextBlueprint<S, S0>,
op1: BlueprintOperator<S, S0, S1>
): IContextBlueprint<S, S1>
export function pipe<S extends Record<PropertyKey, any>, S0, S1, S2>(
source: IContextBlueprint<S, S0>,
op1: BlueprintOperator<S, S0, S1>,
op2: BlueprintOperator<S, S1, S2>
): IContextBlueprint<S, S2>
export function pipe<S extends Record<PropertyKey, any>, S0, S1, S2, S3>(
source: IContextBlueprint<S, S0>,
op1: BlueprintOperator<S, S0, S1>,
op2: BlueprintOperator<S, S1, S2>,
op3: BlueprintOperator<S, S2, S3>
): IContextBlueprint<S, S3>
export function pipe<S extends Record<PropertyKey, any>, S0, S1, S2, S3, S4>(
source: IContextBlueprint<S, S0>,
op1: BlueprintOperator<S, S0, S1>,
op2: BlueprintOperator<S, S1, S2>,
op3: BlueprintOperator<S, S2, S3>,
op4: BlueprintOperator<S, S3, S4>
): IContextBlueprint<S, S4>
export function pipe<S extends Record<PropertyKey, any>, S0, S1, S2, S3, S4, S5>(
source: IContextBlueprint<S, S0>,
op1: BlueprintOperator<S, S0, S1>,
op2: BlueprintOperator<S, S1, S2>,
op3: BlueprintOperator<S, S2, S3>,
op4: BlueprintOperator<S, S3, S4>,
op5: BlueprintOperator<S, S4, S5>
): IContextBlueprint<S, S5>
export function pipe<S extends Record<PropertyKey, any>, S0, S1, S2, S3, S4, S5, S6>(
source: IContextBlueprint<S, S0>,
op1: BlueprintOperator<S, S0, S1>,
op2: BlueprintOperator<S, S1, S2>,
op3: BlueprintOperator<S, S2, S3>,
op4: BlueprintOperator<S, S3, S4>,
op5: BlueprintOperator<S, S4, S5>,
op6: BlueprintOperator<S, S5, S6>
): IContextBlueprint<S, S6>
export function pipe<S extends Record<PropertyKey, any>, S0, S1, S2, S3, S4, S5, S6, S7>(
source: IContextBlueprint<S, S0>,
op1: BlueprintOperator<S, S0, S1>,
op2: BlueprintOperator<S, S1, S2>,
op3: BlueprintOperator<S, S2, S3>,
op4: BlueprintOperator<S, S3, S4>,
op5: BlueprintOperator<S, S4, S5>,
op6: BlueprintOperator<S, S5, S6>,
op7: BlueprintOperator<S, S6, S7>
): IContextBlueprint<S, S7>
export function pipe<S extends Record<PropertyKey, any>, S0, S1, S2, S3, S4, S5, S6, S7, S8>(
source: IContextBlueprint<S, S0>,
op1: BlueprintOperator<S, S0, S1>,
op2: BlueprintOperator<S, S1, S2>,
op3: BlueprintOperator<S, S2, S3>,
op4: BlueprintOperator<S, S3, S4>,
op5: BlueprintOperator<S, S4, S5>,
op6: BlueprintOperator<S, S5, S6>,
op7: BlueprintOperator<S, S6, S7>,
op8: BlueprintOperator<S, S7, S8>
): IContextBlueprint<S, S8>
export function pipe<S extends Record<PropertyKey, any>, S0, S1, S2, S3, S4, S5, S6, S7, S8, S9>(
source: IContextBlueprint<S, S0>,
op1: BlueprintOperator<S, S0, S1>,
op2: BlueprintOperator<S, S1, S2>,
op3: BlueprintOperator<S, S2, S3>,
op4: BlueprintOperator<S, S3, S4>,
op5: BlueprintOperator<S, S4, S5>,
op6: BlueprintOperator<S, S5, S6>,
op7: BlueprintOperator<S, S6, S7>,
op8: BlueprintOperator<S, S7, S8>,
op9: BlueprintOperator<S, S8, S9>
): IContextBlueprint<S, S9>
export function pipe<S extends Record<PropertyKey, any>, S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10>(
source: IContextBlueprint<S, S0>,
op1: BlueprintOperator<S, S0, S1>,
op2: BlueprintOperator<S, S1, S2>,
op3: BlueprintOperator<S, S2, S3>,
op4: BlueprintOperator<S, S3, S4>,
op5: BlueprintOperator<S, S4, S5>,
op6: BlueprintOperator<S, S5, S6>,
op7: BlueprintOperator<S, S6, S7>,
op8: BlueprintOperator<S, S7, S8>,
op9: BlueprintOperator<S, S8, S9>,
op10: BlueprintOperator<S, S9, S10>
): IContextBlueprint<S, S10>

Defined in: src/core/pipe.ts

source

Seed blueprint, typically from createContext().

operators

BlueprintOperator functions applied in order (op1, then op2, and so on).

import { createContext, pipe, withProvider, withBridge, withStorage } from '@sandlada/document-context'
const blueprint = pipe(
createContext({ count: 0 }),
withProvider('logger', () => new ConsoleLogger()),
withBridge({ properties: { count: 'dataset.count' } }),
withStorage({ adapter: 'localStorage', key: 'counter' })
)

Class decorator that tags a class as the provider for a token.

Attaches __serviceToken / __serviceOptions metadata to the constructor without registering anything: pair it with a manual withProvider(token, (s) => new Target(s)) (or provideClass) that reads the metadata, or with a framework integration that scans decorated classes. Does not instantiate the class by itself.

export function provide<K extends string | ServiceToken<any>>(
token: K,
options?: { lifecycle?: ServiceLifecycle; multi?: boolean }
);

Defined in: src/core/decorators.ts

import { provide } from '@sandlada/document-context'
@provide('logger', { lifecycle: 'singleton' })
class Logger {}

Pure Phase 1 operator that registers a class constructor as a service.

Thin wrapper over withProvider(token, (session) => new Ctor(session)). Prefer this over hand-written factories when the service is naturally a class. For decorator style (@provide on the class plus lazy @inject-decorated properties), see provide and injectDecorator.

Note: this module’s decorator alias inject (re-exported as injectDecorator) resolves services on class instances via this.element ?? this.target, while dom/inject’s function-style inject(token)(elementOrSession) resolves from an explicit target. They share a name but take different targets; the decorator form is for class bodies, the function form for everywhere else.

export function provideClass<
K extends string | ServiceToken<any>,
T,
S extends Record<PropertyKey, any>,
ExistingServices
>(
token: K,
ClassConstructor: Constructor<T>,
options?: { lifecycle?: ServiceLifecycle; multi?: boolean }
): (blueprint: IContextBlueprint<S, ExistingServices>) => IContextBlueprint<S, any>;

Defined in: src/core/decorators.ts

import { createContext, pipe, provideClass, mount, inject } from '@sandlada/document-context'
class Logger {
constructor(private session: unknown) {}
log(msg: string) { console.log(msg) }
}
const blueprint = pipe(
createContext({ count: 0 }),
provideClass('logger', Logger, { lifecycle: 'singleton' })
)
const session = mount(blueprint)(document.getElementById('app')!)
inject('logger')(session)

Returns the non-fatal runtime error stream for a session.

The stream is a ReplaySubject<DocumentContextError>(20) held on session internals, so late subscribers replay up to the last 20 errors. Sources include mount plugin/hook failures, dispose cleanup failures, storage parse failures, and bridge errors. Fatal DI throws (UnknownServiceError, CircularDependencyError) still throw synchronously at the call site and are not duplicated here.

Exposes only the Observable interface; the underlying subject stays encapsulated and RxJS never leaks into the public API beyond this type.

export function readErrorStream<S extends Record<PropertyKey, any>, Services>(
session: ISession<S, Services>
): Observable<DocumentContextError>;

Defined in: src/core/dispose.ts

import { readErrorStream } from '@sandlada/document-context'
const sub = readErrorStream(session).subscribe((err) => {
console.error(`[${err.code}]`, err.message, err.resolutionGuide)
})

Registers a live session under a data-context-key for suspend/resuscitate.

Keyed sessions survive host detachment: instead of immediate disposal, the observer parks them in the suspended state for a 50ms TTL during which resuscitateKeyedSession() can reattach them to a new host with state intact. Overwrites any previous session under the same key.

export function registerKeyedSession(
key: string,
session: ISession<any, any>
): void;

Defined in: src/core/resuscitation.ts

import { registerKeyedSession } from '@sandlada/document-context'
registerKeyedSession('cart-panel', session)

Registers a global runtime plugin executed during every mount().

Plugins run in registration order on each mount. Synchronous throws are caught and routed to the session error stream as MOUNT_PLUGIN_ERROR, so one failing plugin never prevents the session from mounting.

export function registerMountPlugin(plugin: MountPlugin): () => void;

Defined in: src/core/mount.ts

import { registerMountPlugin } from '@sandlada/document-context'
const unregister = registerMountPlugin((session) => {
console.log('mounted on', session.target)
return () => console.log('disposed')
})
unregister()

Drops the element-to-session mapping (called once per dispose()). The session handle itself stays usable for isDisposed checks; only the reverse lookup is removed.


export function removeElementSession(element: HTMLElement): void;

Defined in: src/core/session-internal.ts

Reattaches a suspended keyed session to a new host element (resuscitation).

Clears the suspended flag, repoints internal target at newTarget, re-caches the session under the new element, replays the queued dirty updates accumulated via update() during suspension (in order, then emits once), runs blueprint resuscitate hooks as (session, newTarget), and dispatches a bubbling, composed context-resuscitate CustomEvent with detail: { session, newTarget }. Hook throws are sandboxed.

Returns undefined when no session is registered under key, when the registered session is disposed, or when its internals are gone.

export function resuscitateKeyedSession<S extends Record<PropertyKey, any> = Record<PropertyKey, any>>(
key: string,
newTarget: HTMLElement
): ISession<S, any> | undefined;

Defined in: src/core/resuscitation.ts

import { resuscitateKeyedSession } from '@sandlada/document-context'
const replacement = document.querySelector('[data-context-key="cart-panel"]') as HTMLElement
const session = resuscitateKeyedSession('cart-panel', replacement)

Curried synchronous snapshot reader in Data-Last form select(selector)(session).

Applies the pure selector projection to the current state value. When no internal session exists (for example a bare blueprint seed), it falls back to session.blueprint.initialState. In development, object results that are not already frozen are shallow-frozen before returning, so downstream mutation throws early instead of corrupting the store.

The selector must stay pure: no DOM access, no I/O, no mutation of state.

export function select<S extends Record<PropertyKey, any>, R>(
selector: (state: S) => R
): <Services>(session: ISession<S, Services>) => R;

Defined in: src/core/select.ts

import { select } from '@sandlada/document-context'
const selectCount = select((s: { count: number }) => s.count)
const count = selectCount(session)
const selectDouble = select((s: { count: number }) => s.count * 2)

Caches a session handle under its host element (called once per mount(), re-pointed on resuscitation).


export function setElementSession<S extends Record<PropertyKey, any> = Record<PropertyKey, any>>(
element: HTMLElement,
session: ISession<S, any>
): void;

Defined in: src/core/session-internal.ts

Associates internals with a session handle (called once per mount()).


export function setInternalSession<S extends Record<PropertyKey, any> = Record<PropertyKey, any>>(
session: ISession<S, any>,
state: IInternalSessionState<S>
): void;

Defined in: src/core/session-internal.ts

Subscribes a listener to reactive state changes in Data-Last form subscribe(listener)(session).

Backed by the session BehaviorSubject, so the listener fires immediately with the current state and again on every subsequent update(), bridge write, storage hydration, or resuscitation flush. The returned function unsubscribes exactly that listener; call it during dispose cleanups to avoid leaks.

Disposed sessions yield a no-op unsubscribe without invoking the listener, matching the update() silent-no-op contract.

export function subscribe<S extends Record<PropertyKey, any>>(
listener: (state: S) => void
): <Services>(session: ISession<S, Services>) => () => void;

Defined in: src/core/subscribe.ts

import { subscribe } from '@sandlada/document-context'
const unsubscribe = subscribe((s: { count: number }) => {
console.log('count is', s.count)
})(session)
unsubscribe()

Removes the session parked under a data-context-key without disposing it.

Use when a keyed host is permanently retired and its suspended session should no longer be resuscitable. Safe to call for unknown keys (no-op).

export function unregisterKeyedSession(key: string): void;

Defined in: src/core/resuscitation.ts

import { unregisterKeyedSession } from '@sandlada/document-context'
unregisterKeyedSession('cart-panel')

Curried immutable state updater in Data-Last form update(updater)(session).

Accepts either a partial object (shallow-merged) or a transition function (prevState) => partial | full. The next state is always a fresh object ({ ...current, ...partial }, frozen in development) and is emitted through the session BehaviorSubject, waking subscribers, bridges, and storage.

Disposal and suspension contract: updates on a disposed session (or one whose internals are gone) silently no-op and return false, so dangling async closures cannot throw. Updates arriving while the session is suspended (keyed host detached within its 50ms TTL) are additionally appended to the dirty queue for replay on resuscitation, while still emitting to the live subject.

export function update<S extends Record<PropertyKey, any>>(
updater: Partial<S> | ((prevState: S) => Partial<S> | S)
): <Services>(session: ISession<S, Services>) => boolean;

Defined in: src/core/update.ts

import { update } from '@sandlada/document-context'
const increment = update<{ count: number }>((s) => ({ count: s.count + 1 }))
increment(session)
update({ theme: 'dark' })(session)

Pure Phase 1 operator that registers an asynchronous service provider.

Identical to withProvider except the factory returns a Promise<T>. In-flight promises are coalesced per token and evicted immediately on rejection, so concurrent injectAsync() callers share one attempt and a failure self-heals for the next retry. The service type accumulates as Promise<T>; synchronous inject() against this token throws AsyncServiceNotReadyError and callers must use injectAsync().

export function withAsyncProvider<
K extends string | ServiceToken<any>,
T,
S extends Record<PropertyKey, any>,
ExistingServices
>(
token: K,
asyncFactory: (session: ISession<S, ExistingServices>) => Promise<T>,
options?: IWithAsyncProviderOptions
): (
blueprint: IContextBlueprint<S, ExistingServices>
) => IContextBlueprint<
S,
ExistingServices &
(K extends ServiceToken<infer R>
? Record<K['name'], Promise<R>>
: K extends string
? Record<K, Promise<T>>
: {})
>;

Defined in: src/core/async-provider.ts

import { createContext, pipe, withAsyncProvider, mountAsync, injectAsync } from '@sandlada/document-context'
const blueprint = pipe(
createContext({ count: 0 }),
withAsyncProvider('remote-config', async () => fetch('/config.json').then((r) => r.json()))
)
const session = await mountAsync(blueprint)(document.getElementById('app')!)
const config = await injectAsync('remote-config')(session)

Pure Phase 1 operator that registers a lifecycle hook callback.

Appends { event, handler } to blueprint.hooks with structural sharing. Multiple calls for the same event accumulate: mount handlers run FIFO, dispose handlers run LIFO. Valid events are mount, dispose, suspend, resuscitate, and adopt; the handler signature is checked against ILifecycleHooks<S, Services>[Event].

export function withHook<
Event extends LifecycleEventName,
S extends Record<PropertyKey, any>,
Services
>(
event: Event,
handler: NonNullable<ILifecycleHooks<S, Services>[Event]>
): (blueprint: IContextBlueprint<S, Services>) => IContextBlueprint<S, Services>;

Defined in: src/core/hooks.ts

import { withHook } from '@sandlada/document-context'
const blueprint = pipe(
base,
withHook('mount', (session) => {
const timer = setInterval(() => console.log('tick'), 1000)
return () => clearInterval(timer)
})
)

Pure Phase 1 operator that registers a synchronous service provider.

Clones the blueprint with structural sharing and adds (or replaces) the entry in providers. The factory runs lazily on first inject() and receives the owning session, so it may itself call inject() / select(). Recursive re-entry of the same token throws CircularDependencyError. String tokens are typed via the ServiceRegistry augmentation; branded ServiceToken arguments infer their value type from the token.

export function withProvider<
K extends string | ServiceToken<any>,
T,
S extends Record<PropertyKey, any>,
ExistingServices
>(
token: K,
factory: (session: ISession<S, ExistingServices>) => T,
options?: IWithProviderOptions
): (
blueprint: IContextBlueprint<S, ExistingServices>
) => IContextBlueprint<
S,
ExistingServices &
(K extends ServiceToken<infer R>
? Record<K['name'], R>
: K extends string
? Record<K, T>
: {})
>;

Defined in: src/core/providers.ts

import { createContext, pipe, withProvider, mount, inject } from '@sandlada/document-context'
const blueprint = pipe(
createContext({ count: 0 }),
withProvider('logger', () => new ConsoleLogger(), { lifecycle: 'singleton' })
)
const session = mount(blueprint)(document.getElementById('app')!)
const logger = inject('logger')(session)