Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/shadow-objects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- renamed `useResource()` to `createResource()` in `ShadowObjectParams` interface
- renamed interface `ShadowObjectParams` to `ShadowObjectCreationAPI` for clarity and consistency with the concept of the _Shadow Object Creation API_
- renamed `useResource()` to `createResource()` in `ShadowObjectCreationAPI` interface

## [0.23.0] - 2025-11-26

Expand Down
14 changes: 7 additions & 7 deletions packages/shadow-objects/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,20 @@ The **Registry** maps **Tokens** to **Shadow Object Constructors**.

### 1. Defining Shadow Objects

You can define a Shadow Object as a **Function** or a **Class**. Both receive a `ShadowObjectParams` object containing the API methods.
You can define a Shadow Object as a **Function** or a **Class**. Both receive a `ShadowObjectCreationAPI` object containing the API methods.

#### Function-based (Recommended)

```typescript
import { ShadowObjectParams } from "@spearwolf/shadow-objects";
import { ShadowObjectCreationAPI } from "@spearwolf/shadow-objects";

export function MyShadowObject({
useProperty,
useContext,
createEffect,
on,
onDestroy
}: ShadowObjectParams) {
}: ShadowObjectCreationAPI) {

// 1. Read Properties
const title = useProperty("title");
Expand All @@ -75,10 +75,10 @@ export function MyShadowObject({
#### Class-based

```typescript
import { ShadowObjectParams } from "@spearwolf/shadow-objects";
import { ShadowObjectCreationAPI } from "@spearwolf/shadow-objects";

export class MyShadowObject {
constructor({ useProperty, createEffect, onDestroy }: ShadowObjectParams) {
constructor({ useProperty, createEffect, onDestroy }: ShadowObjectCreationAPI) {
const title = useProperty("title");

createEffect(() => {
Expand All @@ -94,9 +94,9 @@ export class MyShadowObject {
}
```

### 2. The `ShadowObjectParams` API
### 2. The Shadow Object Creation API

The `ShadowObjectParams` object provides all necessary tools to interact with the Entity, the View, and the Context system.
The `ShadowObjectCreationAPI` object provides all necessary tools to interact with the Entity, the View, and the Context system.

| Method | Description |
| :--- | :--- |
Expand Down
78 changes: 39 additions & 39 deletions packages/shadow-objects/src/in-the-dark/Kernel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {emit, on} from '@spearwolf/eventize';
import {createSignal, type Signal, type SignalReader, value} from '@spearwolf/signalize';
import {afterEach, describe, expect, it, vi} from 'vitest';
import {MessageToView} from '../constants.js';
import type {ShadowObjectParams} from '../types.js';
import type {ShadowObjectCreationAPI} from '../types.js';
import {generateUUID} from '../utils/generateUUID.js';
import {onCreate, onDestroy} from './events.js';
import {Kernel, type MessageToViewEvent} from './Kernel.js';
Expand Down Expand Up @@ -246,17 +246,17 @@ describe('Kernel', () => {
});
});

describe('ShadowObjectParams API', () => {
describe('Shadow Object Creation API', () => {
describe('useProperty', () => {
it('should return a signal reader for entity property', () => {
const registry = new Registry();
const kernel = new Kernel(registry);

let capturedPropertyReader: ReturnType<ShadowObjectParams['useProperty']> | undefined;
let capturedPropertyReader: ReturnType<ShadowObjectCreationAPI['useProperty']> | undefined;

@ShadowObject({registry, token: 'testUseProperty'})
class TestUseProperty {
constructor({useProperty}: ShadowObjectParams) {
constructor({useProperty}: ShadowObjectCreationAPI) {
capturedPropertyReader = useProperty('testProp');
}
}
Expand All @@ -278,12 +278,12 @@ describe('Kernel', () => {
const registry = new Registry();
const kernel = new Kernel(registry);

let reader1: ReturnType<ShadowObjectParams['useProperty']> | undefined;
let reader2: ReturnType<ShadowObjectParams['useProperty']> | undefined;
let reader1: ReturnType<ShadowObjectCreationAPI['useProperty']> | undefined;
let reader2: ReturnType<ShadowObjectCreationAPI['useProperty']> | undefined;

@ShadowObject({registry, token: 'testUsePropertyCache'})
class TestUsePropertyCache {
constructor({useProperty}: ShadowObjectParams) {
constructor({useProperty}: ShadowObjectCreationAPI) {
reader1 = useProperty('testProp');
reader2 = useProperty('testProp');
}
Expand All @@ -304,11 +304,11 @@ describe('Kernel', () => {
const registry = new Registry();
const kernel = new Kernel(registry);

let capturedProps: Record<string, ReturnType<ShadowObjectParams['useProperty']>> | undefined;
let capturedProps: Record<string, ReturnType<ShadowObjectCreationAPI['useProperty']>> | undefined;

@ShadowObject({registry, token: 'testUseProperties'})
class TestUseProperties {
constructor({useProperties}: ShadowObjectParams) {
constructor({useProperties}: ShadowObjectCreationAPI) {
capturedProps = useProperties({foo: 'propA', bar: 'propB'});
}
}
Expand All @@ -334,19 +334,19 @@ describe('Kernel', () => {
const kernel = new Kernel(registry);

const contextName = Symbol('testContext');
let capturedContext: ReturnType<ShadowObjectParams['useContext']> | undefined;
let capturedContext: ReturnType<ShadowObjectCreationAPI['useContext']> | undefined;

@ShadowObject({registry, token: 'parentProvider'})
class ParentProvider {
constructor({provideContext}: ShadowObjectParams) {
constructor({provideContext}: ShadowObjectCreationAPI) {
provideContext(contextName, 'contextValue');
}
}
expect(ParentProvider).toBeDefined();

@ShadowObject({registry, token: 'childConsumer'})
class ChildConsumer {
constructor({useContext}: ShadowObjectParams) {
constructor({useContext}: ShadowObjectCreationAPI) {
capturedContext = useContext(contextName);
}
}
Expand All @@ -373,19 +373,19 @@ describe('Kernel', () => {

const contextName = 'signalContext';
const sourceSignal = createSignal('initial');
let capturedContext: ReturnType<ShadowObjectParams['useContext']> | undefined;
let capturedContext: ReturnType<ShadowObjectCreationAPI['useContext']> | undefined;

@ShadowObject({registry, token: 'signalProvider'})
class SignalProvider {
constructor({provideContext}: ShadowObjectParams) {
constructor({provideContext}: ShadowObjectCreationAPI) {
provideContext(contextName, sourceSignal.get);
}
}
expect(SignalProvider).toBeDefined();

@ShadowObject({registry, token: 'signalConsumer'})
class SignalConsumer {
constructor({useContext}: ShadowObjectParams) {
constructor({useContext}: ShadowObjectCreationAPI) {
capturedContext = useContext(contextName);
}
}
Expand All @@ -411,12 +411,12 @@ describe('Kernel', () => {
const registry = new Registry();
const kernel = new Kernel(registry);

let ctx1: ReturnType<ShadowObjectParams['useContext']> | undefined;
let ctx2: ReturnType<ShadowObjectParams['useContext']> | undefined;
let ctx1: ReturnType<ShadowObjectCreationAPI['useContext']> | undefined;
let ctx2: ReturnType<ShadowObjectCreationAPI['useContext']> | undefined;

@ShadowObject({registry, token: 'testContextCache'})
class TestContextCache {
constructor({useContext}: ShadowObjectParams) {
constructor({useContext}: ShadowObjectCreationAPI) {
ctx1 = useContext('myContext');
ctx2 = useContext('myContext');
}
Expand All @@ -438,19 +438,19 @@ describe('Kernel', () => {
const kernel = new Kernel(registry);

const contextName = 'parentOnlyContext';
let capturedParentContext: ReturnType<ShadowObjectParams['useParentContext']> | undefined;
let capturedParentContext: ReturnType<ShadowObjectCreationAPI['useParentContext']> | undefined;

@ShadowObject({registry, token: 'parentCtxProvider'})
class ParentCtxProvider {
constructor({provideContext}: ShadowObjectParams) {
constructor({provideContext}: ShadowObjectCreationAPI) {
provideContext(contextName, 'parentValue');
}
}
expect(ParentCtxProvider).toBeDefined();

@ShadowObject({registry, token: 'childCtxConsumer'})
class ChildCtxConsumer {
constructor({useParentContext}: ShadowObjectParams) {
constructor({useParentContext}: ShadowObjectCreationAPI) {
capturedParentContext = useParentContext(contextName);
}
}
Expand All @@ -475,19 +475,19 @@ describe('Kernel', () => {
const kernel = new Kernel(registry);

const globalCtxName = 'globalContext';
let capturedGlobalCtx: ReturnType<ShadowObjectParams['useContext']> | undefined;
let capturedGlobalCtx: ReturnType<ShadowObjectCreationAPI['useContext']> | undefined;

@ShadowObject({registry, token: 'globalProvider'})
class GlobalProvider {
constructor({provideGlobalContext}: ShadowObjectParams) {
constructor({provideGlobalContext}: ShadowObjectCreationAPI) {
provideGlobalContext(globalCtxName, 'globalValue');
}
}
expect(GlobalProvider).toBeDefined();

@ShadowObject({registry, token: 'globalConsumer'})
class GlobalConsumer {
constructor({useContext}: ShadowObjectParams) {
constructor({useContext}: ShadowObjectCreationAPI) {
capturedGlobalCtx = useContext(globalCtxName);
}
}
Expand All @@ -512,19 +512,19 @@ describe('Kernel', () => {

const globalCtxName = 'globalSignalContext';
const sourceSignal = createSignal('globalInitial');
let capturedGlobalCtx: ReturnType<ShadowObjectParams['useContext']> | undefined;
let capturedGlobalCtx: ReturnType<ShadowObjectCreationAPI['useContext']> | undefined;

@ShadowObject({registry, token: 'globalSignalProvider'})
class GlobalSignalProvider {
constructor({provideGlobalContext}: ShadowObjectParams) {
constructor({provideGlobalContext}: ShadowObjectCreationAPI) {
provideGlobalContext(globalCtxName, sourceSignal.get);
}
}
expect(GlobalSignalProvider).toBeDefined();

@ShadowObject({registry, token: 'globalSignalConsumer'})
class GlobalSignalConsumer {
constructor({useContext}: ShadowObjectParams) {
constructor({useContext}: ShadowObjectCreationAPI) {
capturedGlobalCtx = useContext(globalCtxName);
}
}
Expand Down Expand Up @@ -559,7 +559,7 @@ describe('Kernel', () => {

@ShadowObject({registry, token: 'testResource'})
class TestResource {
constructor({createResource}: ShadowObjectParams) {
constructor({createResource}: ShadowObjectCreationAPI) {
resourceSignal = createResource(createFn, cleanupFn);
}
}
Expand Down Expand Up @@ -587,7 +587,7 @@ describe('Kernel', () => {

@ShadowObject({registry, token: 'testUndefinedResource'})
class TestUndefinedResource {
constructor({createResource}: ShadowObjectParams) {
constructor({createResource}: ShadowObjectCreationAPI) {
createResource(createFn, cleanupFn);
}
}
Expand All @@ -611,7 +611,7 @@ describe('Kernel', () => {

@ShadowObject({registry, token: 'testEffect'})
class TestEffect {
constructor({createEffect}: ShadowObjectParams) {
constructor({createEffect}: ShadowObjectCreationAPI) {
createEffect(() => {
effectFn(testSignal.get());
});
Expand Down Expand Up @@ -639,7 +639,7 @@ describe('Kernel', () => {

@ShadowObject({registry, token: 'testEffectDestroy'})
class TestEffectDestroy {
constructor({createEffect}: ShadowObjectParams) {
constructor({createEffect}: ShadowObjectCreationAPI) {
createEffect(() => {
effectFn(testSignal.get());
});
Expand Down Expand Up @@ -669,7 +669,7 @@ describe('Kernel', () => {

@ShadowObject({registry, token: 'testCreateSignal'})
class TestCreateSignal {
constructor({createSignal: cs}: ShadowObjectParams) {
constructor({createSignal: cs}: ShadowObjectCreationAPI) {
createdSignal = cs<string>('initial');
}
}
Expand All @@ -695,7 +695,7 @@ describe('Kernel', () => {

@ShadowObject({registry, token: 'testSignalDestroy'})
class TestSignalDestroy {
constructor({createSignal: cs}: ShadowObjectParams) {
constructor({createSignal: cs}: ShadowObjectCreationAPI) {
createdSignal = cs<string>('test');
}
}
Expand Down Expand Up @@ -724,7 +724,7 @@ describe('Kernel', () => {

@ShadowObject({registry, token: 'testMemo'})
class TestMemo {
constructor({createMemo}: ShadowObjectParams) {
constructor({createMemo}: ShadowObjectCreationAPI) {
memoReader = createMemo<number>(() => sourceSignal.get() * 2);
}
}
Expand Down Expand Up @@ -753,7 +753,7 @@ describe('Kernel', () => {

@ShadowObject({registry, token: 'testOn'})
class TestOn {
constructor({on: subscribe}: ShadowObjectParams) {
constructor({on: subscribe}: ShadowObjectCreationAPI) {
subscribe(emitter, 'testEvent', eventHandler);
}
}
Expand Down Expand Up @@ -783,7 +783,7 @@ describe('Kernel', () => {

@ShadowObject({registry, token: 'testOnce'})
class TestOnce {
constructor({once: subscribeOnce}: ShadowObjectParams) {
constructor({once: subscribeOnce}: ShadowObjectCreationAPI) {
subscribeOnce(emitter, 'singleEvent', eventHandler);
}
}
Expand Down Expand Up @@ -812,7 +812,7 @@ describe('Kernel', () => {

@ShadowObject({registry, token: 'testOnceNoFire'})
class TestOnceNoFire {
constructor({once: subscribeOnce}: ShadowObjectParams) {
constructor({once: subscribeOnce}: ShadowObjectCreationAPI) {
subscribeOnce(emitter, 'neverFiredEvent', eventHandler);
}
}
Expand All @@ -837,7 +837,7 @@ describe('Kernel', () => {

@ShadowObject({registry, token: 'testOnDestroy'})
class TestOnDestroy {
constructor({onDestroy: registerDestroy}: ShadowObjectParams) {
constructor({onDestroy: registerDestroy}: ShadowObjectCreationAPI) {
registerDestroy(destroyCallback);
}
}
Expand All @@ -861,7 +861,7 @@ describe('Kernel', () => {

@ShadowObject({registry, token: 'testMultipleOnDestroy'})
class TestMultipleOnDestroy {
constructor({onDestroy: registerDestroy}: ShadowObjectParams) {
constructor({onDestroy: registerDestroy}: ShadowObjectCreationAPI) {
registerDestroy(() => callOrder.push(1));
registerDestroy(() => callOrder.push(2));
registerDestroy(() => callOrder.push(3));
Expand Down
6 changes: 3 additions & 3 deletions packages/shadow-objects/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export type EntityApi = Pick<
traverse(callback: (entity: EntityApi) => any): void;
};

export interface ShadowObjectParams {
export interface ShadowObjectCreationAPI {
entity: EntityApi;

provideContext<T = unknown>(
Expand Down Expand Up @@ -125,12 +125,12 @@ export interface ShadowObjectParams {
}

export interface ShadowObjectConstructor {
new (params: ShadowObjectParams): {};
new (params: ShadowObjectCreationAPI): {};
displayName?: string;
}

export interface ShadowObjectConstructorFunc {
(params: ShadowObjectParams): object | undefined | void;
(params: ShadowObjectCreationAPI): object | undefined | void;
displayName?: string;
}

Expand Down
Loading