Skip to content

Commit

Permalink
feat!: remove atoms package's dependency on core package (#191)
Browse files Browse the repository at this point in the history
@affects atoms, core, immer, machines, react
  • Loading branch information
bowheart authored Feb 21, 2025
1 parent 78de30c commit 8b206e6
Show file tree
Hide file tree
Showing 33 changed files with 153 additions and 98 deletions.
13 changes: 3 additions & 10 deletions packages/atoms/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# `@zedux/atoms`

The core atomic model of Zedux. This is a standalone package, meaning it's the only package you need to install to use Zedux's atomic model. It includes the Zedux core store package as well as all APIs related to signals, atoms, and ecosystems.
The core atomic model of Zedux. This is a standalone package, meaning it's the only package you need to install to use Zedux's atomic model. It includes all APIs related to signals, atoms, and ecosystems.

This package is framework-independent, though many of its APIs are heavily inspired by React.

Expand All @@ -16,8 +16,6 @@ pnpm add @zedux/atoms # pnpm

If you're using React, you probably want to install the [`@zedux/react` package](https://www.npmjs.com/package/@zedux/react) instead, which includes everything from this package and more.

This package has a direct dependency on the [`@zedux/core` package](https://www.npmjs.com/package/@zedux/core). If you install that directly, ensure its version exactly matches your `@zedux/atoms` version to prevent installing duplicate packages.

## Usage

See the [top-level README](https://github.com/Omnistac/zedux) for a general overview of Zedux.
Expand All @@ -41,11 +39,7 @@ instance.destroy()

## Exports

This package includes and re-exports everything from the following package:

- [`@zedux/core`](https://www.npmjs.com/package/@zedux/core)

On top of this, `@zedux/atoms` exports the following APIs and many helper types for working with them in TypeScript:
`@zedux/atoms` exports the following APIs and many helper types for working with them in TypeScript:

### Classes

Expand Down Expand Up @@ -86,11 +80,10 @@ On top of this, `@zedux/atoms` exports the following APIs and many helper types

### Utils

- [`getEcosystem()`](https://omnistac.github.io/zedux/docs/api/utils/internal-utils#getecosystem)
- [`getDefaultEcosystem()`](https://omnistac.github.io/zedux/docs/api/utils/getDefaultEcosystem)
- [`getInternals()`](https://omnistac.github.io/zedux/docs/api/utils/internal-utils#getinternals)
- [`setInternals()`](https://omnistac.github.io/zedux/docs/api/utils/internal-utils#setinternals)
- [`untrack()`](https://omnistac.github.io/zedux/docs/api/utils/internal-utils#untrack)
- [`wipe()`](https://omnistac.github.io/zedux/docs/api/utils/internal-utils#wipe)

## For Authors

Expand Down
3 changes: 0 additions & 3 deletions packages/atoms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
"bugs": {
"url": "https://github.com/Omnistac/zedux/issues"
},
"dependencies": {
"@zedux/core": "2.0.0-beta.4"
},
"exports": {
".": {
"import": "./dist/esm/index.js",
Expand Down
3 changes: 1 addition & 2 deletions packages/atoms/src/classes/AtomApi.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { is } from '@zedux/core'
import {
AtomInstanceTtl,
AtomApiGenerics,
Prettify,
ExportsConfig,
} from '@zedux/atoms/types/index'
import { INITIALIZING, prefix } from '@zedux/atoms/utils/general'
import { INITIALIZING, is, prefix } from '@zedux/atoms/utils/general'
import { getEvaluationContext } from '../utils/evaluationContext'

const wrapExports = <T extends Record<string, any>>(
Expand Down
11 changes: 6 additions & 5 deletions packages/atoms/src/classes/Ecosystem.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { is, isPlainObject, Job } from '@zedux/core'
import {
AnyAtomInstance,
AnyAtomTemplate,
Expand Down Expand Up @@ -31,6 +30,7 @@ import {
EcosystemEvents,
InternalEvaluationReason,
GetNode,
Job,
} from '../types/index'
import {
External,
Expand All @@ -48,6 +48,7 @@ import {
RUN_START,
RESET_START,
RUN_END,
is,
} from '../utils/general'
import { IdGenerator } from './IdGenerator'
import { Scheduler } from './Scheduler'
Expand Down Expand Up @@ -552,10 +553,10 @@ export class Ecosystem<Context extends Record<string, any> | undefined = any>
dehydratedState: Record<string, any>,
config?: { retroactive?: boolean }
) {
if (DEV && !isPlainObject(dehydratedState)) {
throw new TypeError(
'Zedux: ecosystem.hydrate() - first parameter must be a plain object'
)
if (DEV && (!dehydratedState || typeof dehydratedState !== 'object')) {
throw new TypeError('Zedux: Expected an object', {
cause: dehydratedState,
})
}

this.hydration = { ...this.hydration, ...dehydratedState }
Expand Down
3 changes: 2 additions & 1 deletion packages/atoms/src/classes/GraphNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import {
GraphEdge,
GraphEdgeConfig,
InternalEvaluationReason,
Job,
ListenerConfig,
NodeFilter,
NodeFilterOptions,
NodeGenerics,
} from '@zedux/atoms/types/index'
import { is, Job } from '@zedux/core'
import { Ecosystem } from './Ecosystem'
import {
ACTIVE,
Expand All @@ -21,6 +21,7 @@ import {
ExplicitExternal,
INITIALIZING,
InternalLifecycleStatus,
is,
statusMap,
} from '../utils/general'
import { AtomTemplateBase } from './templates/AtomTemplateBase'
Expand Down
5 changes: 3 additions & 2 deletions packages/atoms/src/classes/IdGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { isPlainObject } from '@zedux/core'
import { GraphNode } from './GraphNode'

/**
Expand Down Expand Up @@ -49,7 +48,9 @@ export class IdGenerator {
return JSON.stringify(params, (_, param) => {
if (!param) return param
if (param.izn) return (param as GraphNode).id
if (!isPlainObject(param)) {

// if the prototype has no prototype, it's likely not a plain object:
if (Object.getPrototypeOf(param.constructor.prototype)) {
if (!acceptComplexParams || Array.isArray(param)) return param
if (typeof param === 'function') return this.cacheFn(param)
if (typeof param === 'object') return this.cacheClass(param)
Expand Down
2 changes: 1 addition & 1 deletion packages/atoms/src/classes/MappedSignal.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Settable } from '@zedux/core'
import {
AnyNodeGenerics,
AtomGenerics,
InternalEvaluationReason,
Mutatable,
SendableEvents,
Settable,
Transaction,
UndefinedEvents,
} from '../types/index'
Expand Down
7 changes: 6 additions & 1 deletion packages/atoms/src/classes/Scheduler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Job, Scheduler as SchedulerInterface } from '@zedux/core'
import { Job } from '@zedux/atoms/types/index'
import { Ecosystem } from './Ecosystem'

// temporarily copied from @zedux/core. TODO: remove
interface SchedulerInterface {
scheduleNow(newJob: Job): void
}

export class Scheduler implements SchedulerInterface {
/**
* `I`nterrupt - currently interrupt jobs only have one use - to defer `set`
Expand Down
2 changes: 1 addition & 1 deletion packages/atoms/src/classes/Signal.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Settable } from '@zedux/core'
import {
AtomGenerics,
Mutatable,
NodeGenerics,
SendableEvents,
Settable,
Transaction,
UndefinedEvents,
} from '../types/index'
Expand Down
7 changes: 5 additions & 2 deletions packages/atoms/src/classes/instances/AtomInstance.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { is, Observable, Settable } from '@zedux/core'
import {
Mutatable,
SendableEvents,
Expand All @@ -14,6 +13,7 @@ import {
DehydrationOptions,
AnyAtomGenerics,
InternalEvaluationReason,
Settable,
} from '@zedux/atoms/types/index'
import {
ACTIVE,
Expand All @@ -22,6 +22,7 @@ import {
INITIALIZING,
INVALIDATE,
Invalidate,
is,
prefix,
PROMISE_CHANGE,
PromiseChange,
Expand Down Expand Up @@ -533,7 +534,9 @@ export class AtomInstance<
}

// ttl is an observable; destroy as soon as it emits
const subscription = (ttl as Observable).subscribe(() => {
const subscription = (
ttl as { subscribe: (cb: () => void) => { unsubscribe: () => void } }
).subscribe(() => {
this.c = undefined
this.destroy()
})
Expand Down
3 changes: 1 addition & 2 deletions packages/atoms/src/classes/proxies.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { RecursivePartial } from '@zedux/core'
import { Transaction, MutatableTypes } from '../types/index'
import { Transaction, MutatableTypes, RecursivePartial } from '../types/index'

export type ParentProxy<State> = {
t: Transaction[]
Expand Down
2 changes: 1 addition & 1 deletion packages/atoms/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import {
scheduleStaticDependents,
} from './utils/graph'

export * from '@zedux/core'
export * from './classes/index'
export * from './factories/index'
export * from './injectors/index'
export * from './types/index'
export { untrack } from './utils/evaluationContext'
export { is } from './utils/general'

type Internals = { c: EvaluationContext; g: Ecosystem }

Expand Down
7 changes: 3 additions & 4 deletions packages/atoms/src/injectors/injectPromise.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { detailedTypeof, RecursivePartial } from '@zedux/core'
import { api } from '../factories/api'
import {
getErrorPromiseState,
Expand All @@ -13,6 +12,7 @@ import {
MapEvents,
None,
PromiseState,
RecursivePartial,
} from '../types/index'
import { injectEffect } from './injectEffect'
import { injectMemo } from './injectMemo'
Expand Down Expand Up @@ -134,9 +134,8 @@ export const injectPromise: {

if (DEV && typeof promise?.then !== 'function') {
throw new TypeError(
`Zedux: injectPromise expected callback to return a promise. Received ${detailedTypeof(
promise
)}`
'Zedux: injectPromise expected callback to return a promise',
{ cause: promise }
)
}

Expand Down
2 changes: 1 addition & 1 deletion packages/atoms/src/injectors/injectSelf.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { is } from '@zedux/core'
import { AnyAtomInstance, PartialAtomInstance } from '../types/index'
import { getEvaluationContext } from '../utils/evaluationContext'
import { AtomInstance } from '../classes/instances/AtomInstance'
import { is } from '../utils/general'

/**
* An unrestricted injector (can actually be used in loops and if statements).
Expand Down
2 changes: 1 addition & 1 deletion packages/atoms/src/types/events.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { RecursivePartial } from '@zedux/core'
import { GraphNode } from '../classes/GraphNode'
import {
AnyNodeGenerics,
Expand All @@ -8,6 +7,7 @@ import {
ListenerConfig,
NodeGenerics,
Prettify,
RecursivePartial,
} from './index'

export type CatchAllListener<G extends NodeGenerics> = (
Expand Down
38 changes: 37 additions & 1 deletion packages/atoms/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Observable, Settable } from '@zedux/core'
import { AtomTemplateBase } from '../classes/templates/AtomTemplateBase'
import { AtomApi } from '../classes/AtomApi'
import { Ecosystem } from '../classes/Ecosystem'
Expand Down Expand Up @@ -319,6 +318,31 @@ export type IonStateFactory<G extends Omit<AtomGenerics, 'Node' | 'Template'>> =
...params: G['Params']
) => AtomApi<AtomGenericsToAtomApiGenerics<G>> | Signal<G> | G['State']

export interface Job {
/**
* `W`eight - the weight of the node (for EvaluateGraphNode jobs).
* UpdateExternalDependent jobs also use this to track the order they were
* added as dependents, since that's the order they should evaluate in.
*/
W?: number

/**
* `j`ob - the actual task to run.
*/
j: () => void

/**
* `T`ype - the job type. Different types get different priorities in the
* scheduler.
*
* 0 - UpdateStore
* 1 - InformSubscribers
* 2 - EvaluateGraphNode
* 3 - UpdateExternalDependent
*/
T: 0 | 1 | 2 | 3
}

export type LifecycleStatus = 'Active' | 'Destroyed' | 'Initializing' | 'Stale'

export interface ListenerConfig {
Expand All @@ -335,6 +359,10 @@ export interface MutableRefObject<T = any> {
current: T
}

export interface Observable<T = any> {
subscribe(subscriber: (value: T) => any): { unsubscribe: () => void }
}

export interface NodeFilterOptions {
exclude?: (AnyAtomTemplate | AtomSelectorOrConfig | string)[]
excludeTags?: string[]
Expand Down Expand Up @@ -389,6 +417,10 @@ export interface PromiseState<T> {

export type PromiseStatus = 'error' | 'loading' | 'success'

export type RecursivePartial<T> = T extends Record<string, any>
? { [P in keyof T]?: RecursivePartial<T[P]> }
: T

export type Ref<T = any> = MutableRefObject<T>

export interface RefObject<T = any> {
Expand All @@ -403,6 +435,10 @@ export type Selectable<State = any, Params extends any[] = any> =
Template: AtomSelectorOrConfig<State, Params>
}>

export type Settable<State = any, StateIn = State> =
| ((state: StateIn) => State)
| State

export type StateHookTuple<State, Exports> = [
State,
ExportsInfusedSetter<State, Exports>
Expand Down
17 changes: 7 additions & 10 deletions packages/atoms/src/utils/ecosystem.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { detailedTypeof, is } from '@zedux/core'
import {
AnyAtomInstance,
AnyAtomTemplate,
Expand All @@ -13,7 +12,7 @@ import type { Ecosystem } from '../classes/Ecosystem'
import { GraphNode } from '../classes/GraphNode'
import { getSelectorKey, SelectorInstance } from '../classes/SelectorInstance'
import { getEvaluationContext } from './evaluationContext'
import { DESTROYED } from './general'
import { DESTROYED, is } from './general'

const getContextualizedId = (
ecosystem: Ecosystem,
Expand Down Expand Up @@ -61,11 +60,9 @@ export const getNode = <G extends AtomGenerics>(

if (DEV) {
if (typeof params !== 'undefined' && !Array.isArray(params)) {
throw new TypeError(
`Zedux: Expected atom params to be an array. Received ${detailedTypeof(
params
)}`
)
throw new TypeError('Zedux: Expected atom params to be an array', {
cause: params,
})
}
}

Expand Down Expand Up @@ -165,9 +162,9 @@ export const getNode = <G extends AtomGenerics>(
}
}

throw new TypeError(
`Zedux: Expected a template or node. Received ${detailedTypeof(template)}`
)
throw new TypeError('Zedux: Expected a template, selector, or graph node', {
cause: template,
})
}

const resolveAtom = <A extends AnyAtomTemplate>(
Expand Down
Loading

0 comments on commit 8b206e6

Please # to comment.