@adobe/data-ai 0.10.21 → 0.10.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/README.md +26 -31
- package/.claude/rules/app.md +46 -0
- package/.claude/rules/archetypes.md +5 -3
- package/.claude/rules/data-modelling.md +8 -7
- package/.claude/rules/ecs.md +11 -7
- package/.claude/rules/element.md +6 -5
- package/.claude/rules/features/data/components.md +51 -0
- package/.claude/rules/features/data/entities.md +37 -0
- package/.claude/rules/features/data/index.md +18 -70
- package/.claude/rules/features/data/resources.md +34 -0
- package/.claude/rules/features/data/values.md +36 -0
- package/.claude/rules/features/ecs/actions.md +31 -0
- package/.claude/rules/features/ecs/computed.md +31 -0
- package/.claude/rules/features/ecs/conformance.md +85 -0
- package/.claude/rules/features/ecs/core.md +43 -0
- package/.claude/rules/features/ecs/index.md +72 -0
- package/.claude/rules/features/{services/main-service → ecs}/indexes.md +6 -7
- package/.claude/rules/features/ecs/services.md +36 -0
- package/.claude/rules/features/{services/main-service → ecs}/systems.md +61 -47
- package/.claude/rules/features/ecs/transactions.md +32 -0
- package/.claude/rules/features/index.md +104 -150
- package/.claude/rules/features/services/index.md +52 -84
- package/.claude/rules/features/spec/index.md +161 -0
- package/.claude/rules/features/ui/binding-element.md +5 -5
- package/.claude/rules/features/ui/index.md +8 -3
- package/.claude/rules/features/ui/lazy-wrapper.md +2 -2
- package/.claude/rules/features/ui/presentation-test.md +1 -0
- package/.claude/rules/features/ui/presentation.md +1 -0
- package/.claude/rules/global/function-references.md +2 -2
- package/.claude/rules/global/namespace.md +3 -3
- package/.claude/rules/global/react.md +4 -4
- package/.claude/rules/lit.md +2 -2
- package/.claude/rules/plugin-modelling.md +2 -0
- package/.claude/rules/service.md +2 -0
- package/.claude/rules/versioning.md +8 -6
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +3 -3
- package/skills/build-actions/SKILL.md +7 -8
- package/skills/build-app-entry/SKILL.md +12 -8
- package/skills/build-computed/SKILL.md +8 -7
- package/skills/build-core-database/SKILL.md +18 -15
- package/skills/build-data/SKILL.md +14 -13
- package/skills/build-feature/SKILL.md +2 -1
- package/skills/build-game/SKILL.md +6 -4
- package/skills/build-indexes/SKILL.md +6 -7
- package/skills/build-service-database/SKILL.md +8 -8
- package/skills/build-services/SKILL.md +9 -7
- package/skills/build-spec/SKILL.md +23 -0
- package/skills/build-systems/SKILL.md +12 -18
- package/skills/build-transactions/SKILL.md +8 -8
- package/skills/build-ui/SKILL.md +5 -4
- package/skills/review/SKILL.md +3 -3
- package/.claude/rules/features/data/state.md +0 -209
- package/.claude/rules/features/services/main-service/actions.md +0 -70
- package/.claude/rules/features/services/main-service/archetypes.md +0 -49
- package/.claude/rules/features/services/main-service/components.md +0 -41
- package/.claude/rules/features/services/main-service/computed.md +0 -82
- package/.claude/rules/features/services/main-service/conformance.md +0 -95
- package/.claude/rules/features/services/main-service/core-database.md +0 -48
- package/.claude/rules/features/services/main-service/index.md +0 -187
- package/.claude/rules/features/services/main-service/resources.md +0 -28
- package/.claude/rules/features/services/main-service/services.md +0 -25
- package/.claude/rules/features/services/main-service/transactions.md +0 -59
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
paths:
|
|
3
|
-
- '**/features/*/services/main-service/main-service.ts'
|
|
4
|
-
- '**/features/*/services/main-service/*-database/*-database.ts'
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# services/main-service/ — the feature service (ECS implementation)
|
|
8
|
-
|
|
9
|
-
`main-service/` is the feature's single entrypoint service (see `../index.md`),
|
|
10
|
-
implemented as a reactive Entity-Component-System — an implementation detail
|
|
11
|
-
behind the service. Everything ECS-specific lives here: the schema that stores `data/` types as
|
|
12
|
-
entity state (`core-database/`), the derived and mutating logic over it
|
|
13
|
-
(`indexes/`, `transactions/`, `computed/`), the database-bound service factories
|
|
14
|
-
(`services/`), the async orchestrators (`actions/`), and the layered plugins
|
|
15
|
-
that compose it all.
|
|
16
|
-
|
|
17
|
-
The distinction from `data/`: a `data/` type is ECS-agnostic (a plain
|
|
18
|
-
serializable value); an ECS construct is *how that type is stored and operated
|
|
19
|
-
on* inside a database. `mark: PlayerMark.schema` is not a data-model fact — it
|
|
20
|
-
is "the ECS keeps a PlayerMark here".
|
|
21
|
-
|
|
22
|
-
## The core schema — `core-database/`
|
|
23
|
-
|
|
24
|
-
A feature's whole schema — every component, resource, and archetype — lives in
|
|
25
|
-
one `core-database/` folder: `core-database.ts` plus one file per facet the
|
|
26
|
-
feature uses (all three facets are optional on `Database.Plugin.create`):
|
|
27
|
-
|
|
28
|
-
```
|
|
29
|
-
core-database/
|
|
30
|
-
core-database.ts # Database.Plugin.create({ components?, resources?, archetypes? }) → CoreDatabase
|
|
31
|
-
components.ts # export const components = Database.components({ … })
|
|
32
|
-
resources.ts # export const resources = Database.resources({ … })
|
|
33
|
-
archetypes.ts # export const archetypes = Database.archetypes(components, { … })
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
`core-database.ts` is a thin assembler; each facet file exports one typed object
|
|
37
|
-
built by a `Database.*` helper. No per-item files, no barrels. Create only the
|
|
38
|
-
facet files the feature needs — a singleton feature with no entities is just
|
|
39
|
-
`resources.ts` + `core-database.ts` (no empty `components({})` boilerplate).
|
|
40
|
-
|
|
41
|
-
### Schema scopes
|
|
42
|
-
|
|
43
|
-
State divides on two orthogonal axes — **scope** (shared with peers vs. local to
|
|
44
|
-
this client) and **lifetime** (durable across reloads vs. ephemeral) — giving
|
|
45
|
-
four scopes, each stamping a flag pair:
|
|
46
|
-
|
|
47
|
-
| scope | shared? | durable? | flags |
|
|
48
|
-
|-------|:--:|:--:|-------|
|
|
49
|
-
| **document** | yes | yes | — |
|
|
50
|
-
| **settings** | no | yes | `nonShared` |
|
|
51
|
-
| **presence** | yes | no | `nonPersistent` |
|
|
52
|
-
| **session** | no | no | `nonPersistent` + `nonShared` |
|
|
53
|
-
|
|
54
|
-
`Database.components` / `Database.resources` group declarations by scope and
|
|
55
|
-
stamp the flags. Every scope key is optional — declare only the ones you use
|
|
56
|
-
(most features are document-only):
|
|
57
|
-
|
|
58
|
-
```ts
|
|
59
|
-
// components.ts
|
|
60
|
-
export const components = Database.components({
|
|
61
|
-
document: { todo: True.schema, name: Name.schema, complete: Boolean.schema },
|
|
62
|
-
session: { dragPosition: DragPosition.schema }, // omit settings / presence
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// resources.ts — entries must carry a `default` (enforced at the call)
|
|
66
|
-
export const resources = Database.resources({
|
|
67
|
-
settings: { displayCompleted: Boolean.schema },
|
|
68
|
-
});
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
A component/resource *value* is a plain `data/`-schema re-export (or a small
|
|
72
|
-
inline literal); its scope — and therefore its flags — is stated only by which
|
|
73
|
-
group it sits in. Hover a scope key for its meaning. The database does not yet
|
|
74
|
-
act on these flags (see the `nonShared` schema note); they model
|
|
75
|
-
shared/local × durable/ephemeral now, and the durable-shared subset
|
|
76
|
-
(`document`) is the set a bare store could be rebuilt from for reconciliation.
|
|
77
|
-
|
|
78
|
-
**Shared columns live in `data/`.** A column two features share (e.g. a `name`
|
|
79
|
-
on both todos and users) is a `data/` type both reference by identity
|
|
80
|
-
(`Name.schema`), so `combinePlugins` dedupes it. Only the `document` scope
|
|
81
|
-
preserves schema identity (it applies no flags) — never share a
|
|
82
|
-
settings/presence/session column across features by identity.
|
|
83
|
-
|
|
84
|
-
### Archetypes
|
|
85
|
-
|
|
86
|
-
`archetypes.ts` declares the feature's entity shapes with
|
|
87
|
-
`Database.archetypes(components, { … })`, which validates every key against the
|
|
88
|
-
component map and preserves each archetype's literal tuple — no per-archetype
|
|
89
|
-
`as const`. Archetypes are a packing convenience, not serialized data; one may
|
|
90
|
-
span scopes (a document entity with an ephemeral `dragPosition` slot). An
|
|
91
|
-
archetype mirroring a `data/` row type carries a private compile-time
|
|
92
|
-
drift-guard (see `archetypes.md`).
|
|
93
|
-
|
|
94
|
-
## Layered composition (behaviour)
|
|
95
|
-
|
|
96
|
-
Above the schema, behaviour is a chain of `Database.Plugin`s, each its own
|
|
97
|
-
**namespace folder** — `<layer>-database/` holding the eponymous
|
|
98
|
-
`<layer>-database.ts` (the plugin, which `extends` the previous layer) plus the
|
|
99
|
-
facet folder it adds. **Name each layer for the facet it adds — never for the
|
|
100
|
-
feature or app.** A feature creates only the layers it uses:
|
|
101
|
-
|
|
102
|
-
```
|
|
103
|
-
main-service/
|
|
104
|
-
core-database/ # the schema (above)
|
|
105
|
-
index-database/ # extends core; indexes/
|
|
106
|
-
transaction-database/ # extends index; transactions/
|
|
107
|
-
service-database/ # extends transaction; services/
|
|
108
|
-
computed-database/ # extends service; computed/
|
|
109
|
-
action-database/ # extends computed; actions/
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
Each exports a namespace mirroring the plugin — the composed object is a
|
|
113
|
-
`Database` (the `@adobe/data` term) even though the folder is `services/main-service/`:
|
|
114
|
-
|
|
115
|
-
```ts
|
|
116
|
-
export type IndexDatabase = Database.Plugin.ToDatabase<typeof indexDatabasePlugin>;
|
|
117
|
-
export namespace IndexDatabase {
|
|
118
|
-
export const plugin = indexDatabasePlugin;
|
|
119
|
-
export type Store = Database.Plugin.ToStore<typeof indexDatabasePlugin>;
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### The assembled database — `MainService`
|
|
124
|
-
|
|
125
|
-
*Which* layer is topmost varies with the facets a feature uses (service /
|
|
126
|
-
computed / action / system). Consumers — `ui/`, the app entry, `services/main-service/conformance/`
|
|
127
|
-
— need the *whole* feature database but must not name that layer, or adding or
|
|
128
|
-
dropping a layer rewrites them all. Alias it once from the **folder-eponymous
|
|
129
|
-
`main-service.ts`** (per `global/namespace.md`, a folder's primary export lives in
|
|
130
|
-
its same-named file), as a namespace so consumers reach `MainService.plugin` /
|
|
131
|
-
`MainService.Store`:
|
|
132
|
-
|
|
133
|
-
```ts
|
|
134
|
-
// services/main-service/main-service.ts
|
|
135
|
-
export { SystemDatabase as MainService } from "./system-database/system-database.js";
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Every consumer references **`MainService`** (`.plugin`, `.Store`) — never the
|
|
139
|
-
topmost layer. Add or drop a layer → change only this one line.
|
|
140
|
-
|
|
141
|
-
**Cross-feature naming.** Inside a feature the core and assembled databases are
|
|
142
|
-
the bare `CoreDatabase` / `MainService`. When *another* feature or package
|
|
143
|
-
imports one — a peer built on it, the base `imports` a peer's `core-database`, a
|
|
144
|
-
downstream package — reference it **feature-qualified**: `<Feature>CoreDatabase`
|
|
145
|
-
/ `<Feature>MainService`, re-exported so from the feature's barrel
|
|
146
|
-
(`export { CoreDatabase as TodoCoreDatabase } from "…/core-database.js"`) — so
|
|
147
|
-
two features' `CoreDatabase`s never collide.
|
|
148
|
-
|
|
149
|
-
## What each layer takes as input
|
|
150
|
-
|
|
151
|
-
- **`plugin`** — every layer `extends` the previous layer's `plugin`; that is
|
|
152
|
-
the one universal thread up the chain.
|
|
153
|
-
- **`Store`** (via `ToStore`) — used *only* by **transactions** (`t:
|
|
154
|
-
<Layer>.Store`). A store carries the index handles and the initiating
|
|
155
|
-
`userId`, so a store *is* the transaction context; there is no separate
|
|
156
|
-
transaction-context type. Use **`CoreDatabase.Store`** for a transaction that
|
|
157
|
-
reads/writes entities, resources, or archetypes; **`IndexDatabase.Store`** the
|
|
158
|
-
moment it reads an index.
|
|
159
|
-
- **the whole `Database`** — **services**, **computed**, and **actions** each
|
|
160
|
-
take `service: <Layer>` (the lowest layer whose database exposes what they
|
|
161
|
-
read/call): services read observables and call transactions; computed reads
|
|
162
|
-
`service.observe.*` and, once it needs a service's own reactive surface,
|
|
163
|
-
`service.services.*`; actions call `service.services.*` then
|
|
164
|
-
`service.transactions.*`.
|
|
165
|
-
|
|
166
|
-
Each subfolder has its own rule. Modelling a plugin's authored vs. derived
|
|
167
|
-
surface is covered by `plugin-modelling.md`.
|
|
168
|
-
|
|
169
|
-
## Facets are flat until they aren't
|
|
170
|
-
|
|
171
|
-
A **behavioural** facet folder (`indexes/`, `transactions/`, `computed/`,
|
|
172
|
-
`actions/`) holds one file per item plus an `index.ts` barrel — flat while that
|
|
173
|
-
reads well, roughly up to half a dozen items. When a cohesive cluster grows past
|
|
174
|
-
that, promoting it into a themed subfolder with its own `index.ts` (re-exported
|
|
175
|
-
by the facet barrel) is a standard move — only once the folder actually feels
|
|
176
|
-
crowded:
|
|
177
|
-
|
|
178
|
-
```
|
|
179
|
-
transactions/
|
|
180
|
-
order/ # a cohesive cluster: fractional-ordering helpers
|
|
181
|
-
index.ts next-order.ts normalize-order.ts select-ordered-todos.ts
|
|
182
|
-
create-todo.ts drag-todo.ts …
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
The **schema** facets (components / resources / archetypes) are *single files*
|
|
186
|
-
in `core-database/`, not folders. A whole feature should stay small enough that
|
|
187
|
-
subfoldering rarely bites — grow by adding features, not by bloating one.
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
paths:
|
|
3
|
-
- '**/features/*/services/main-service/**/core-database/resources.ts'
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# core-database/resources.ts — the feature's resources
|
|
7
|
-
|
|
8
|
-
A resource is a singleton component — one instance held on a single entity. One
|
|
9
|
-
file, one export: `resources`, built with `Database.resources(...)`. It is
|
|
10
|
-
identical to `Database.components` except **every entry must carry a `default`**
|
|
11
|
-
— a missing `default` is a compile error here, at the declaration site.
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
import { Database } from "@adobe/data/ecs";
|
|
15
|
-
import { Boolean } from "@adobe/data/schema";
|
|
16
|
-
|
|
17
|
-
export const resources = Database.resources({
|
|
18
|
-
settings: { displayCompleted: Boolean.schema }, // per-device view toggle
|
|
19
|
-
});
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
- Give each resource a `default` — spread the data-type's schema and add one, or
|
|
23
|
-
bind a `data/` / primitive schema that already carries a sensible default.
|
|
24
|
-
- **Scope = which group it sits in** (see `services/main-service/index.md`). A per-device
|
|
25
|
-
preference is `settings` (durable, `nonShared`); a synced-but-unsaved value is
|
|
26
|
-
`presence`; a saved shared value is `document`.
|
|
27
|
-
- Because there is only ever one instance, linear-memory packing buys nothing —
|
|
28
|
-
never choose a struct schema for efficiency here.
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
paths:
|
|
3
|
-
- '**/features/*/services/main-service/service-database/services/**/*.ts'
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# database/services/ — database-bound service factories
|
|
7
|
-
|
|
8
|
-
One factory per file: `create<Name>Service(service, …)` that binds a service
|
|
9
|
-
implementation to a live database — reading its observables and invoking
|
|
10
|
-
its transactions. This is where the async `services/` contracts (or
|
|
11
|
-
framework services like `AgenticService`) are wired to ECS state.
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
export const createAgentService = (service: TransactionDatabase, mark: PlayerMark): AgenticService =>
|
|
15
|
-
/* … reads service.observe.*, calls service.transactions.* … */;
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Type `service` on the lowest layer exposing what the factory reads/calls (never
|
|
19
|
-
the `service-database` itself — that would be a cycle).
|
|
20
|
-
|
|
21
|
-
An `index.ts` barrel re-exports the factories; `service-database.ts`
|
|
22
|
-
registers them under the `services` facet, each keyed by the name
|
|
23
|
-
consumers read it as (`service.services.agent`). Defining a standalone
|
|
24
|
-
service contract (not yet bound to a database) belongs in the feature
|
|
25
|
-
`services/` layer.
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
paths:
|
|
3
|
-
- '**/features/*/services/main-service/transaction-database/transactions/**/*.ts'
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# database/transactions/ — atomic mutations
|
|
7
|
-
|
|
8
|
-
One mutation per file: a function taking the transaction store as its
|
|
9
|
-
first argument and pure `data/` args, returning `void`. It reads and
|
|
10
|
-
writes `t.resources` / entities; all its writes commit or roll back
|
|
11
|
-
together.
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
import { PlayMoveArgs } from "../../../data/play-move-args/play-move-args.js";
|
|
15
|
-
import type { CoreDatabase } from "../../core-database/core-database.js";
|
|
16
|
-
|
|
17
|
-
export const playMove = (t: CoreDatabase.Store, { index }: PlayMoveArgs) => {
|
|
18
|
-
// guard, then mutate t.resources / entities / archetypes
|
|
19
|
-
};
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
- Type the store parameter as `<Layer>.Store`: **`CoreDatabase.Store`** for a
|
|
23
|
-
transaction that reads/writes entities, resources, or archetypes (the common
|
|
24
|
-
case — all schema lives in `core-database`); **`IndexDatabase.Store`** the
|
|
25
|
-
moment it reads an index. A store *is* the transaction context: it carries the
|
|
26
|
-
index handles and the initiating `t.userId`. There is no separate
|
|
27
|
-
transaction-context type.
|
|
28
|
-
- Keep transactions idempotent under replay where sync/P2P applies —
|
|
29
|
-
validate and silently return on illegal or out-of-turn input rather
|
|
30
|
-
than throwing.
|
|
31
|
-
- Decisions come from pure `data/` helpers; the transaction only applies the
|
|
32
|
-
result — read the touched slice, call the `data/` transform, write the diff.
|
|
33
|
-
- Keep transaction files **single-export** (the `transactions/` barrel is
|
|
34
|
-
`export *`-ed into the plugin facet, so a second export would pollute it).
|
|
35
|
-
- **ECS-based feature (no `data/state/`):** there are no conformance cases, so
|
|
36
|
-
**every transaction carries its own unit test** — `Store.create(plugin)`, run the
|
|
37
|
-
transaction, assert the resulting resources/entities/archetypes. That test is the
|
|
38
|
-
direct substitute for the conformance oracle (see `../../index.md`, Two modes). The
|
|
39
|
-
bullet below applies only to **state-based** features.
|
|
40
|
-
- **Conformance is wired once, centrally, and auto-paired** — not per-file. The
|
|
41
|
-
feature's single `conformance/conformance.test.ts` `Conformance.checkFeature(spec)`
|
|
42
|
-
call conforms transactions: it pulls them off **`plugin.transactions`** (the
|
|
43
|
-
registered facet), pairs each to the **same-named** `data/state` transition, and
|
|
44
|
-
conforms it — seed `fromState(before)`, apply, `Match.assert` `toState ≡ after`
|
|
45
|
-
(state only; service effects are asserted through the action). There is **no**
|
|
46
|
-
`define`/`conforms` adapter and **no** `covers` guard. A transaction taking
|
|
47
|
-
**entity ids** takes them under the transition's own arg key (`{ id }`); the runner
|
|
48
|
-
resolves each id the case's `args` schema marks via the id→entity map `fromState` returns. A
|
|
49
|
-
transaction with **no same-named transition** is infrastructure (`setInput`,
|
|
50
|
-
`setBounds`) or the drag UI op (`dragTodo`) or system-dispatched — it is simply
|
|
51
|
-
**skipped**, no guard needed. Don't add a per-item adapter for a renamed op; add a
|
|
52
|
-
thin same-named transaction instead (see `conformance.md`).
|
|
53
|
-
- An `index.ts` barrel feeds the `transactions` plugin facet — so it must
|
|
54
|
-
re-export **only** the mutations. A read/query helper shared by several
|
|
55
|
-
transactions (`readShip`, `readBoard` — a `(t) => value` function) may live
|
|
56
|
-
beside them in `transactions/`, but keep it **out of the barrel**, or it gets
|
|
57
|
-
registered as a dispatchable transaction. (These are related helpers, so they
|
|
58
|
-
belong here, not in a separate folder — `global/cohesion.md`'s peer-level caution
|
|
59
|
-
doesn't apply.)
|