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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "data-monorepo",
"version": "0.10.8",
"version": "0.10.9",
"private": true,
"engines": {
"node": ">=24"
Expand Down
2 changes: 1 addition & 1 deletion packages/data-ai/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "adobe-data-ai",
"version": "0.10.8",
"version": "0.10.9",
"description": "Architecture skills for @adobe/data — data-oriented modelling, archetype iteration, hot-path performance, and related conventions.",
"author": {
"name": "Adobe"
Expand Down
2 changes: 1 addition & 1 deletion packages/data-ai/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/data-ai",
"version": "0.10.8",
"version": "0.10.9",
"description": "Cross-agent architecture skills for @adobe/data — installable as a Claude Code plugin or copied into any Agent-Skills-compatible agent (Cursor, Codex).",
"type": "module",
"private": false,
Expand Down
2 changes: 1 addition & 1 deletion packages/data-gpu/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/data-gpu",
"version": "0.10.8",
"version": "0.10.9",
"description": "Adobe data WebGPU plugins and types for graphics and compute",
"type": "module",
"private": false,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// © 2026 Adobe. MIT License. See /LICENSE for details.

import { describe, it, expect } from "vitest";
import { Quat } from "@adobe/data/math";
import type { Schema } from "@adobe/data/schema";
import { interpolate } from "./interpolate.js";

describe("interpolate", () => {
it("resolves a schema's named interpolator via the registry (Quat linear ⇒ slerp)", () => {
const a: Quat = [0, 0, 0, 1]; // identity
const b: Quat = [0, Math.SQRT1_2, 0, Math.SQRT1_2]; // 90° about Y
// Must equal slerp exactly — proving it dispatched to the registered
// "slerp" function, not the componentwise-lerp fallback (which would
// produce a different, non-normalized result).
expect(interpolate(Quat.schema, "linear", a, b, 0.5)).toEqual(Quat.slerp(a, b, 0.5));
});

it("falls back to componentwise lerp when no interpolator is named", () => {
const schema: Schema = { type: "array", items: { type: "number" } };
expect(interpolate(schema, "linear", [0, 10], [10, 20], 0.5)).toEqual([5, 15]);
});

it("throws on a named interpolator that isn't registered", () => {
const schema: Schema = { type: "number", interpolators: { linear: "nope" } };
expect(() => interpolate(schema, "linear", 0, 1, 0.5)).toThrow(/unknown interpolator "nope"/);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import type { Schema } from "@adobe/data/schema";
import type { InterpolationMode } from "../interpolation-mode/interpolation-mode.js";
import { componentwiseLerp } from "./componentwise-lerp.js";
import { interpolatorRegistry } from "./interpolator-registry.js";

/**
* Dispatches to a schema-declared interpolator if present, otherwise falls
* back to a sensible default: `step` returns `next`, `linear` walks the schema
* and lerps numeric leaves. `cubicSpline` requires a schema override.
* and lerps numeric leaves. `cubicSpline` requires a schema override. A schema
* names its interpolator (pure JSON); the name is resolved via the registry.
*/
export function interpolate(
schema: Schema,
Expand All @@ -16,8 +18,12 @@ export function interpolate(
next: any,
t: number,
): any {
const custom = schema.interpolators?.[mode];
if (custom) return custom(prev, next, t);
const name = schema.interpolators?.[mode];
if (name !== undefined) {
const custom = interpolatorRegistry[name];
if (!custom) throw new Error(`interpolate: unknown interpolator "${name}" for mode "${mode}"`);
return custom(prev, next, t);
}
if (mode === "step") return prev;
if (mode === "linear") return componentwiseLerp(schema, prev, next, t);
throw new Error(`interpolate: schema type "${schema.type}" has no "${mode}" interpolator`);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// © 2026 Adobe. MIT License. See /LICENSE for details.

import { Quat } from "@adobe/data/math";

type Interpolator = (prev: any, next: any, t: number) => any;

/**
* Resolves the serializable interpolator NAMES a pure-JSON `Schema` may declare
* (e.g. `Quat.schema`'s `interpolators.linear = "slerp"`) to their functions.
* The schema names the interpolator; the animation system owns the behavior.
*/
export const interpolatorRegistry: Readonly<Record<string, Interpolator>> = {
slerp: Quat.slerp,
};
2 changes: 1 addition & 1 deletion packages/data-lit/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/data-lit",
"version": "0.10.8",
"version": "0.10.9",
"description": "Adobe data Lit bindings - hooks, elements, decorators",
"type": "module",
"private": false,
Expand Down
2 changes: 1 addition & 1 deletion packages/data-persistence/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/data-persistence",
"version": "0.10.8",
"version": "0.10.9",
"description": "Worker-based incremental persistence layer for @adobe/data ECS over OPFS (browser) and node:fs (server).",
"type": "module",
"sideEffects": false,
Expand Down
2 changes: 1 addition & 1 deletion packages/data-react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/data-react",
"version": "0.10.8",
"version": "0.10.9",
"description": "Adobe data React bindings — hooks and context for ECS database",
"type": "module",
"private": false,
Expand Down
2 changes: 1 addition & 1 deletion packages/data-solid/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/data-solid",
"version": "0.10.8",
"version": "0.10.9",
"description": "Adobe data SolidJS bindings — context and provider for ECS database",
"type": "module",
"private": false,
Expand Down
2 changes: 1 addition & 1 deletion packages/data-sync/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/data-sync",
"version": "0.10.8",
"version": "0.10.9",
"description": "Multi-user real-time synchronisation for @adobe/data ECS — server, client, and in-process loopback.",
"type": "module",
"sideEffects": false,
Expand Down
2 changes: 1 addition & 1 deletion packages/data-testing/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/data-testing",
"version": "0.10.8",
"version": "0.10.9",
"description": "Conformance-testing utilities (Match + Conformance runners) for @adobe/data ECS features",
"type": "module",
"sideEffects": false,
Expand Down
2 changes: 1 addition & 1 deletion packages/data/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/data",
"version": "0.10.8",
"version": "0.10.9",
"description": "Adobe data oriented programming library",
"type": "module",
"sideEffects": false,
Expand Down
5 changes: 3 additions & 2 deletions packages/data/src/math/quat/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

import { F32 } from "../f32/index.js";
import { Schema } from "../../schema/index.js";
import { slerp } from "./slerp.js";

export const schema = {
type: 'array',
items: F32.schema,
minItems: 4,
maxItems: 4,
default: [0, 0, 0, 1], // identity quaternion
// "slerp" (spherical linear interpolation) — the animation system resolves
// this name to Quat.slerp so quaternion tracks interpolate on the 4-sphere.
interpolators: {
linear: slerp,
linear: "slerp",
},
} as const satisfies Schema;

16 changes: 9 additions & 7 deletions packages/data/src/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,15 @@ export interface Schema {
const?: any;
enum?: readonly any[];
layout?: Layout; // Memory layout for typed buffers (std140 or packed)
// Per-type interpolation overrides used by the animation system. Schemas omit
// this when the componentwise lerp / step default is correct (Vec3, scalar, …).
// Quat declares { linear: slerp } so quaternion tracks are interpolated on the
// 4-sphere instead of component-wise.
// Per-type interpolation overrides for the animation system, as serializable
// NAMES (not functions — a Schema is pure JSON data). Each name is resolved to
// an interpolator by the consuming animation system's registry (see
// `@adobe/data-gpu` animation-track). Schemas omit this when the componentwise
// lerp / step default is correct (Vec3, scalar, …); `Quat` declares
// `{ linear: "slerp" }` so quaternion tracks interpolate on the 4-sphere.
interpolators?: {
readonly linear?: (prev: any, next: any, t: number) => any;
readonly step?: (prev: any, next: any, t: number) => any;
readonly cubicSpline?: (prev: any, next: any, t: number) => any;
readonly linear?: string;
readonly step?: string;
readonly cubicSpline?: string;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Overview

`AsyncDataService.createLazy` provides a type-safe way to create lazy-loading wrapper factories for AsyncDataServices. The real service is only loaded when the first property is accessed. Wrapping is driven by the service's **sideloaded schema** — a `Schema` published beside the service (e.g. `MyService.schema`) rather than attached to instances.
`AsyncDataService.createLazy` provides a type-safe way to create lazy-loading wrapper factories for AsyncDataServices. The real service is only loaded when the first property is accessed. Wrapping is driven by the service's **sideloaded schema** — a `Schema` authored beside the service (e.g. `MyService.schema`) and passed to `createLazy`. (Separately, a service may also expose that schema at runtime via the optional base-`Service` `schema` slot, typed `Observe<Schema>` so it can change as the interface evolves.)

## Import

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,10 +410,14 @@ describe('createLazy', () => {

const service = factory();

// The schema slot is Observe<Schema>; subscribing emits the constant synchronously.
let observed: unknown;
service.schema?.((s) => { observed = s; });

assert({
given: 'a lazy service is created',
should: 'expose the same schema without triggering a load',
actual: `${service.schema === schema},${loaded}`,
should: 'expose its schema as a constant observable without triggering a load',
actual: `${observed === schema},${loaded}`,
expected: 'true,false'
});
});
Expand Down
5 changes: 3 additions & 2 deletions packages/data/src/service/async-data-service/create-lazy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,11 @@ export function createLazy<
}

// Build lazy service object. Expose the schema up front (before load) so the
// lazy instance is introspectable without triggering a load.
// lazy instance is introspectable without triggering a load. The base slot is
// `Observe<Schema>`, so publish the static contract as a constant observable.
const lazyService: any = {
serviceName: 'lazy-service',
schema,
schema: Observe.fromConstant(schema),
};

// Wrap each member based on the strategy derived from its schema
Expand Down
17 changes: 11 additions & 6 deletions packages/data/src/service/service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// © 2026 Adobe. MIT License. See /LICENSE for details.

import type { Schema } from "../schema/index.js";
import type { Observe } from "../observe/index.js";

/**
* A service is an object that provides functionality to an application.
Expand All @@ -16,13 +17,17 @@ import type { Schema } from "../schema/index.js";
* A service's shape can be described by a `Schema` (an object schema whose
* property schemas describe each member). The schema is authored beside the
* service (e.g. `MyService.schema`) and validated with `IsValidWithCompleteSchema`;
* a factory may also attach it to the instance via the optional `schema` slot for
* runtime introspection. Both `serviceName` and `schema` are base-`Service`
* metadata, so they are excluded from `AsyncDataService.IsValid` and reserved as
* member names. See `async-data-service/is-valid-with-*-schema.ts`.
* a factory may also expose it at runtime via the optional `schema` slot for
* introspection. It is `Observe<Schema>` — observable — because a service's
* discoverable interface can change at runtime (e.g. once an iframe-projected
* service loads and reveals a different surface). Both `serviceName` and `schema`
* are base-`Service` metadata, so they are excluded from `AsyncDataService.IsValid`
* (which is why `schema` need not be `Data`) and reserved as member names.
* See `async-data-service/is-valid-with-*-schema.ts`.
*/
export interface Service {
readonly serviceName?: string;
/** Optional runtime copy of the service's schema (its authored contract). */
readonly schema?: Schema;
/** Optional observable of the service's schema (its authored contract), which
* may change at runtime as the service's discoverable interface evolves. */
readonly schema?: Observe<Schema>;
}