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.27",
"version": "0.10.28",
"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.27",
"version": "0.10.28",
"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.27",
"version": "0.10.28",
"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.27",
"version": "0.10.28",
"description": "Adobe data WebGPU plugins and types for graphics and compute",
"type": "module",
"private": false,
Expand Down
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.27",
"version": "0.10.28",
"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.27",
"version": "0.10.28",
"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.27",
"version": "0.10.28",
"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-rpc/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@adobe/data-rpc",
"version": "0.10.27",
"version": "0.10.28",
"description": "Schema-driven, bidirectional projection of @adobe/data async data services across a boundary (iframe / MessagePort / Worker). Only Data crosses the wire.",
"type": "module",
"sideEffects": 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.27",
"version": "0.10.28",
"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.27",
"version": "0.10.28",
"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.27",
"version": "0.10.28",
"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.27",
"version": "0.10.28",
"description": "Adobe data oriented programming library",
"type": "module",
"sideEffects": false,
Expand Down
42 changes: 42 additions & 0 deletions packages/data/src/ecs/database/default-factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,48 @@ describe("insert-time default-factory (full plugin chain)", () => {
});
});

// An index whose key or order reads a default-factory column must file the row
// under the value the archetype MINTED, not the omitted insert payload.
describe("indexes over a default-factory component", () => {
const indexedPlugin = Database.Plugin.create({
components: { guid, value },
archetypes: { Node: ["guid", "value"] } as const,
indexes: {
byValue: { key: "value", order: { by: ["guid"] } },
byGuid: { key: "guid" },
},
transactions: {
addNode(t, args: { value: number }) {
return t.archetypes.Node.insert(args);
},
adoptNode(t, args: { value: number; guid: number }) {
return t.archetypes.Node.insert(args);
},
},
});

const create = () => {
let next = 1000;
return Database.create(indexedPlugin, { defaultFactories: { guid: () => next-- } });
};

it("orders a row with a minted default-factory value on insert", () => {
const db = create();
const minted = db.transactions.addNode({ value: 1 }); // guid 1000
const supplied = db.transactions.adoptNode({ value: 1, guid: 2000 });
const mintedLater = db.transactions.addNode({ value: 1 }); // guid 999

expect(db.indexes.byValue.find({ value: 1 })).toEqual([mintedLater, minted, supplied]);
});

it("keys a row by a minted default-factory value on insert", () => {
const db = create();
const minted = db.transactions.addNode({ value: 1 }); // guid 1000

expect(db.indexes.byGuid.find({ guid: 1000 })).toEqual([minted]);
});
});

// Compile-time checks — the payoff for consumers: inside a transaction / system
// body (the mutable store `t`), default-factory components are optional at insert
// while every other component stays required. This is the shape the ~30 insert
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ export interface IndexRegistry {
/**
* Reflect a single insert into the entity's archetype. Dispatches to only
* the indexes applicable to that archetype — O(applicable), not O(indexes).
* Reads the STORED row rather than taking the insert payload: the archetype
* mints `defaultFactory` components the payload omits.
*/
applyInsert(entity: Entity, archetype: IndexableArchetype, values: Readonly<Record<string, unknown>>): void;
applyInsert(entity: Entity, archetype: IndexableArchetype): void;
/**
* Reflect a single update by re-reading the entity's full values. `from`
* is the archetype before the update, `to` the archetype after (they
Expand Down Expand Up @@ -239,9 +241,13 @@ export const createIndexRegistry = (
return "<index>";
};

const applyInsert = (entity: Entity, archetype: IndexableArchetype, values: Readonly<Record<string, unknown>>): void => {
const applyInsert = (entity: Entity, archetype: IndexableArchetype): void => {
if (indexes.size === 0) return;
for (const idx of indexesFor(archetype)) idx.add(entity, values);
const applicable = indexesFor(archetype);
if (applicable.length === 0) return;
const values = read(entity);
if (values === null) return;
for (const idx of applicable) idx.add(entity, values);
};

const applyUpdate = (entity: Entity, from: IndexableArchetype | null, to: IndexableArchetype | null): void => {
Expand Down
9 changes: 5 additions & 4 deletions packages/data/src/ecs/store/public/create-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,10 @@ export function createStore<
// delete). Higher layers (`db.indexes`, `t.indexes`) expose this same
// map by reference.
//
// The reader is only used for `applyUpdate` — the patch passed to
// `store.update` may omit components an index covers, so the registry
// re-reads the full record to recompute the key. Seeding goes through
// The reader serves `applyInsert` and `applyUpdate` — an insert payload may
// omit `defaultFactory` components the archetype mints, and an update patch
// may omit components an index covers, so the registry re-reads the stored
// record to compute the key. Seeding goes through
// `seedIndexFromArchetypes` instead (see below).
const indexRegistry = createIndexRegistry(
// `core.read` already excludes `id` (the identity is never a useful index
Expand Down Expand Up @@ -266,7 +267,7 @@ export function createStore<
// the applicable indexes.
indexRegistry.checkUniqueAvailableForInsert(archetype, values);
const entity = rawInsert(values);
indexRegistry.applyInsert(entity, archetype, values);
indexRegistry.applyInsert(entity, archetype);
return entity;
};
};
Expand Down
Loading