@adobe/data-ai 0.9.86 → 0.9.88

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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  paths:
3
- - 'packages/**/*presentation.ts'
3
+ - '**/*presentation.ts'
4
4
  ---
5
5
 
6
6
  # Presentation authoring
@@ -42,37 +42,7 @@ export function render({ onDismiss, onLayoutChange, onCreate }: { ... }) {}
42
42
 
43
43
  ## Testing
44
44
 
45
- A pure `render(props)` is trivial to test — no DOM, no rendering, no
46
- component lifecycle. Pass stub props (no-op callbacks) and assert on the
47
- **declaration** it returns. Co-locate tests as `<name>-presentation.test.ts`.
48
-
49
- For Lit, wrap the result with `Template.from` (from `@adobe/data-lit`) and
50
- assert on the query, not on positional `.values` indices:
51
-
52
- ```ts
53
- import { describe, it, expect } from "vitest";
54
- import { Template } from "@adobe/data-lit";
55
- import { render } from "./todo-toolbar-presentation.js";
56
-
57
- it("wires the Add button and shows the stats", () => {
58
- const addTodo = () => {};
59
- const t = Template.from(render(props({ addTodo, totalCount: 3, completedCount: 1 })));
60
- expect(t.has("<sp-textfield")).toBe(true); // presence
61
- expect(t.text).toContain("1 / 3 completed"); // static + primitive text
62
- expect(t.values).toContain(addTodo); // callback wired (identity, not values[3])
63
- });
64
- ```
65
-
66
- `Template.Query` exposes `text`, `values`, `children`, `has(fragment)`, and
67
- `find(fragment)`. Prefer **identity** checks (`values` `toContain` a
68
- callback / `nothing` / a bound value) over positions. To exercise a
69
- callback, pull it from `values` and call it with a plain synthetic event —
70
- no mocks, no DOM.
71
-
72
- A presentation that only composes lazy child-element wrappers (or imports
73
- Spectrum components) needs a `// @vitest-environment jsdom` docblock so the
74
- module's custom-element side effects can load — the assertions still never
75
- render or touch the DOM. Presentations of native elements run under Node.
45
+ @see ./presentation-test.md
76
46
 
77
47
  ## Framework bindings
78
48
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  paths:
3
- - 'packages/**/*.ts'
4
- - 'packages/**/*.tsx'
3
+ - '**/*.ts'
4
+ - '**/*.tsx'
5
5
  ---
6
6
 
7
7
  # Pass functions by reference — don't wrap them
@@ -0,0 +1,57 @@
1
+ ---
2
+ paths:
3
+ - '**/use-*.ts'
4
+ - '**/hooks/**/*.ts'
5
+ ---
6
+
7
+ # Element hooks (Lit / `@adobe/data-lit`)
8
+
9
+ Hooks tie **element lifecycle** and **DOM** concerns to reactive updates. This rule covers **where** hooks live, **how** to name and split
10
+ them, and **what** to reuse before writing something new.
11
+
12
+ ---
13
+
14
+ ## Where hooks belong
15
+
16
+ | Location | Role |
17
+ | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
18
+ | **Binding element** (`ApplicationElement` / app `*Element` / `DatabaseElement`) | **Default home** for hooks: `useObservableValues`, refs, resize, visibility, keyboard, etc. Compute values here and pass **plain data + action callbacks** into `presentation.render(...)`. |
19
+ | **Presentation** (`*-presentation.ts`) | **Hook-free** so `render` stays a pure `props → TemplateResult` function. **Exception:** only when unavoidable for performance or platform constraints — document **why** a binding-level hook could not work. |
20
+ | **Shared UI primitives** (plain `LitElement`, not a binding element) | Hooks allowed when the primitive owns DOM behavior; still follow **one hook per file** and the reuse order below. |
21
+
22
+ ---
23
+
24
+ ## Reuse before inventing
25
+
26
+ Search in this order before adding a new hook module:
27
+
28
+ 1. **`src/elements/hooks/`** — shared hooks for Studio / platform elements.
29
+ 2. **`@adobe/data-lit`** — framework-provided hooks and primitives.
30
+ 3. **Application-specific** — under `src/applications/<app>/…` when behavior is owned by that app.
31
+ 4. **Element-local** — `use-<concern>.ts` next to the element when behavior is specific to one component.
32
+
33
+ ---
34
+
35
+ ## One hook, one file
36
+
37
+ - Each custom hook lives in **its own module** with a clear name (`use-element-size.ts`, `use-my-panel-keyboard.ts`).
38
+ - Do **not** bundle unrelated hooks in one file.
39
+ - Export the hook (and any minimal types its signature needs). Avoid dumping unrelated utilities into the same file.
40
+
41
+ ---
42
+
43
+ ## Encapsulation
44
+
45
+ - A hook should own a **single concern**: e.g. "sync width to state", "subscribe to window resize", "focus prompt on open".
46
+ - Prefer **narrow return values** (values + small set of callbacks) over mutating arbitrary element fields.
47
+
48
+ ---
49
+
50
+ ## Execute
51
+
52
+ When adding or changing hook code:
53
+
54
+ 1. Confirm the hook cannot live on a **binding element** (or shared hook package) before putting it in a **presentation** file.
55
+ 2. Run the **reuse** search order above; extend an existing hook if the behavior is the same concern.
56
+ 3. Add or edit **one file per hook**; name with the `use-` prefix consistent with the rest of the repo.
57
+ 4. Keep **binding** `render()` thin — call hooks, then pass results into `presentation.render`.
@@ -0,0 +1,132 @@
1
+ ---
2
+ paths:
3
+ - '**/*.css.ts'
4
+ - '**/*.css'
5
+ - '**/elements/**/*.ts'
6
+ ---
7
+
8
+ # Layout and component types
9
+
10
+ UI components are one of two types with no overlap. Every component is either terminal or layout.
11
+
12
+ ---
13
+
14
+ ## Terminal components
15
+
16
+ - Render their own UI (buttons, inputs, cards, text, etc.)
17
+ - Contain CSS for their appearance
18
+ - **Never** contain any external margin
19
+
20
+ Terminal components are the leaves of the component tree. They own their visual styling and content.
21
+
22
+ ---
23
+
24
+ ## Layout components
25
+
26
+ - Do **not** render any UI themselves
27
+ - Composed only of other layout or terminal components
28
+ - Responsible for interior gaps between their children
29
+ - **Never** contain any external margin
30
+ - Should not need CSS 90% of the time or more — use the standard layout utility classes below.
31
+ - Generally have no business logic and should never re-render — keeps re-renders confined to lower terminal levels
32
+
33
+ **Exception:** Some layout components explicitly manage state (animating, showing/hiding tabs, accordions). Those will have logic and may
34
+ re-render.
35
+
36
+ ---
37
+
38
+ ## Layout utility classes
39
+
40
+ Define `--layout-gap`, `--layout-gutter`, and `--layout-padding` on `:root` (or the root element) with your spacing scale. In Lit, import
41
+ the styles and include them in `export const styles` of the root element or shared base class.
42
+
43
+ ```css
44
+ /* -- Direction --------------------------------------------------------- */
45
+ .stack {
46
+ display: flex;
47
+ flex-direction: column;
48
+ gap: var(--layout-gap);
49
+ }
50
+ .row {
51
+ display: flex;
52
+ flex-direction: row;
53
+ gap: var(--layout-gap);
54
+ }
55
+ .cluster {
56
+ display: flex;
57
+ flex-direction: row;
58
+ flex-wrap: wrap;
59
+ gap: var(--layout-gap);
60
+ }
61
+
62
+ /* -- Gap overrides ----------------------------------------------------- */
63
+ .gap-gutter {
64
+ gap: var(--layout-gutter);
65
+ }
66
+ .gap-padding {
67
+ gap: var(--layout-padding);
68
+ }
69
+ .gap-none {
70
+ gap: 0;
71
+ }
72
+
73
+ /* -- Inset (padding) --------------------------------------------------- */
74
+ .inset {
75
+ padding: var(--layout-padding);
76
+ }
77
+ .inset-gutter {
78
+ padding: var(--layout-gutter);
79
+ }
80
+ .inset-gap {
81
+ padding: var(--layout-gap);
82
+ }
83
+
84
+ /* -- Alignment --------------------------------------------------------- */
85
+ .center {
86
+ justify-content: center;
87
+ align-items: center;
88
+ }
89
+ .spread {
90
+ justify-content: space-between;
91
+ }
92
+ .align-center {
93
+ align-items: center;
94
+ }
95
+ .align-start {
96
+ align-items: flex-start;
97
+ }
98
+ .align-end {
99
+ align-items: flex-end;
100
+ }
101
+
102
+ /* -- Sizing (applied to children) -------------------------------------- */
103
+ .fill {
104
+ flex: 1;
105
+ }
106
+ .fit {
107
+ flex: 0 0 auto;
108
+ }
109
+ ```
110
+
111
+ For custom spacing in terminal components, use `var(--layout-gap)`, `var(--layout-gutter)`, or `var(--layout-padding)` rather than hardcoded
112
+ lengths.
113
+
114
+ ---
115
+
116
+ ## Why this split matters
117
+
118
+ Layout components that rarely re-render are efficient. State changes and user interactions trigger updates at terminal levels; layout
119
+ structure above stays stable. Avoid putting reactive logic in layout components unless the layout itself is dynamic (tabs, accordions,
120
+ animations).
121
+
122
+ ---
123
+
124
+ ## Execute
125
+
126
+ When creating or modifying UI components:
127
+
128
+ 1. Classify as terminal or layout. No overlap.
129
+ 2. Terminal: owns UI and CSS; never external margin.
130
+ 3. Layout: no own UI; only layout/terminal children; owns interior gaps; never external margin.
131
+ 4. Prefer standard layout tokens for layout components; avoid custom CSS when possible.
132
+ 5. Keep layout components free of business logic unless they explicitly manage layout state.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  paths:
3
- - 'packages/**/ui/**/*.ts'
3
+ - '**/ui/**/*.ts'
4
4
  ---
5
5
 
6
6
  # Lazy Lit element pattern
@@ -0,0 +1,115 @@
1
+ ---
2
+ paths:
3
+ - '**/ui/**/*.ts'
4
+ - '**/elements/**/*.ts'
5
+ - '**/*-element.ts'
6
+ ---
7
+
8
+ # Lit element authoring
9
+
10
+ Lit elements live in the `components/` layer per the structure rule. Consume Observe and void actions from plugins per the service rule.
11
+
12
+ ---
13
+
14
+ ## Extending DatabaseElement
15
+
16
+ Binding elements extend `DatabaseElement<typeof myPlugin>` — either directly or via an intermediate base class. The required plugin must be
17
+ specified in the generic.
18
+
19
+ ```ts
20
+ // Direct
21
+ export class HelloWorldElement extends DatabaseElement<typeof helloWorldPlugin> {
22
+ get plugin() {
23
+ return helloWorldPlugin;
24
+ }
25
+ }
26
+
27
+ // Indirect — base extends DatabaseElement, leaf specifies plugin
28
+ export class LayoutElement<T extends MyApplicationPlugin = MyApplicationPlugin> extends CoreApplicationElement<T> {}
29
+ export class ToolbarElement extends LayoutElement<typeof toolbarPlugin> {}
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Binding element vs presentation
35
+
36
+ **Binding element** — the Lit custom element that:
37
+
38
+ - Injects observed values via `useObservableValues`
39
+ - Triggers re-render when those values change
40
+ - Binds action callbacks to the presentation
41
+
42
+ **Presentation** — a pure function (no hooks by default) that receives data and action callbacks as props and returns a `TemplateResult`.
43
+ Keep reactive logic in the binding element; presentation stays pure.
44
+
45
+ ---
46
+
47
+ ## useObservableValues
48
+
49
+ _Most_ binding elements use a **single** `useObservableValues` call. Collect all observed values in one object.
50
+
51
+ **Observe only what you need** — the minimal values required for rendering. For values that may resolve slowly, wrap with
52
+ `Observe.withDefault`.
53
+
54
+ ```ts
55
+ render() {
56
+ const values = useObservableValues(() => ({
57
+ visible: this.service.computed.isViewVisible(name),
58
+ userProfile: this.service.services.authentication.userProfile,
59
+ }));
60
+ if (!values) return;
61
+ return presentation.render({ ...values, toggleView: () => this.service.transactions.setViewVisible({ name, visible: true }) });
62
+ }
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Presentation exports
68
+
69
+ Presentation files ONLY export `render` (and `unlocalized` bundles where appropriate). Nothing else. If you need render args type
70
+ externally, use `Parameters<typeof render>[0]`.
71
+
72
+ **Styles:** Do not import or re-export Lit `CSSResult` from presentations. The binding element imports `*.css.ts` and sets `static styles`
73
+ there.
74
+
75
+ ---
76
+
77
+ ## Action callbacks (not events)
78
+
79
+ Use **verb** or **verbNoun** names — never `on*` prefix. See `.claude/rules/presentation.md` for full examples and rationale.
80
+
81
+ ---
82
+
83
+ ## Lit properties
84
+
85
+ **Almost never** use `@property` on binding elements.
86
+
87
+ **Exception:** Use properties ONLY when needed to bind to the correct entity in the database — e.g. `entity` for table rows, `layer` for
88
+ view hosts. Multiple instances of the same element need a property to identify which entity they represent.
89
+
90
+ ---
91
+
92
+ ## Hooks
93
+
94
+ Canonical rules (reuse order, one hook per file, binding vs presentation): `.claude/rules/hooks.md`.
95
+
96
+ Use hooks from `src/elements/hooks/` and `@adobe/data-lit` instead of `connectedCallback`/`disconnectedCallback`. Use `useEffect` for
97
+ setup/teardown, `useKeyboardEvent` for single key events.
98
+
99
+ ---
100
+
101
+ ## Execute
102
+
103
+ When creating or modifying a Lit element:
104
+
105
+ 1. Extend `DatabaseElement<typeof myPlugin>` (directly or indirectly) with the required plugin specified.
106
+ 2. Split into binding element (reactive) and presentation (pure).
107
+ 3. Use a single `useObservableValues` in the binding element. Observe only minimal values; use `Observe.withDefault` for slow-resolving
108
+ values.
109
+ 4. Pass observed values and action callbacks to presentation.
110
+ 5. Keep presentation pure — no hooks (see `.claude/rules/hooks.md` for exceptions).
111
+ 6. Add `@property` only when entity binding requires it (multiple instances).
112
+ 7. Presentation exports only `render` (and localization bundles where appropriate). Binding element sets `static styles` from `*.css.ts`.
113
+ 8. Add `*-presentation.test.ts` for presentation when appropriate; do not unit test binding elements.
114
+ 9. Never include business logic within binding elements. Move into computed values or action handlers.
115
+ 10. Good binding elements should be extremely small.
@@ -1,160 +1,118 @@
1
1
  ---
2
2
  paths:
3
- - 'packages/**/src/**/*.ts'
3
+ - '**/*.ts'
4
4
  ---
5
5
 
6
6
  # Type namespace pattern
7
7
 
8
- Each type lives in its own folder. The folder's eponymous file is the only
9
- public import surface; a `<Type>` namespace re-exports every helper.
8
+ Single import surface per type: `<type-name>/<type-name>.ts`. Schema in `-schema.ts`, namespace re-export from `public.js`. Types live in
9
+ the `types/` layer.
10
10
 
11
- ```
12
- src/types/<type-name>/
13
- <type-name>.ts # type alias + `export * as <Type> from "./public.js"`
14
- schema.ts # `export const schema = { ... } as const satisfies Schema`
15
- public.ts # re-exports every public helper
16
- <helper>.ts # one declaration per file, camelCase
17
- ```
11
+ ## Authority
18
12
 
19
- ## Example
13
+ This rule defines the canonical pattern. **Follow this standard for all new and refactored type folders.** The codebase may contain legacy
14
+ patterns (e.g., inline `export namespace` in shared files) that do not conform — do not copy those; follow this rule instead.
20
15
 
21
- ```ts
22
- // types/log-level/schema.ts
23
- export const schema = { type: "string", enum: ["debug", "info", "warn", "error"] }
24
- as const satisfies Schema;
25
-
26
- // types/log-level/log-level.ts
27
- import { Schema } from "@adobe/data/schema";
28
- import { schema } from "./schema.js";
29
- export type LogLevel = Schema.ToType<typeof schema>;
30
- export * as LogLevel from "./public.js";
31
-
32
- // types/log-level/public.ts
33
- export { schema } from "./schema.js";
34
- export { is } from "./is.js"; // when external code narrows broader input
35
- export { values } from "./values.js"; // when external code iterates the set
36
- ```
16
+ ## Naming conventions
37
17
 
38
- Consumers import only `LogLevel` from `log-level/log-level.ts` and use it
39
- as both type and namespace (`LogLevel.is(x)`, `LogLevel.values`).
40
-
41
- ## Rules
42
-
43
- - Never import `schema.ts` or `public.ts` from outside the folder.
44
- - Consumers use one import: `import { LogLevel } from "./log-level.js"`.
45
- The same identifier serves as type *and* namespace — a separate
46
- `import type` is unnecessary and produces a duplicate-identifier error.
47
- - Helper files use `import type` from the sibling `<type-name>.ts` to
48
- avoid cycling through `public.ts`.
49
- - Add `is` / `values` / per-member descriptors only when a consumer
50
- actually needs them — not preemptively. A consumer in a not-yet-built
51
- higher layer counts: a presentational descriptor the `ui/` will need
52
- (per-member glyphs, a keypad `values` list) may be authored with the
53
- data type, but give it a one-line doc naming the intended consumer (as
54
- `player-mark/mark-color.ts` does) so the need is recorded, not guessed.
55
-
56
- ## Strict single public export per file
57
-
58
- **One public export per file** — this is strict, not a preference. A file
59
- that *declares* exports exactly one public symbol (private declarations
60
- inside it are fine and are tested through the public export). The reason is
61
- cohesion: when several declarations share one file, each pulls its own
62
- external imports to the top and the file becomes a jumble that belongs to no
63
- single concept. One declaration → one file → imports that are about that one
64
- thing.
65
-
66
- More than one public export in a file is allowed **only when explicitly
67
- approved on a per-file or per-file-type basis** — the deliberate, named
68
- patterns below, nothing else. There is no "shared imports" or "cohesive set"
69
- escape hatch: a folder of sibling pure functions over one type (e.g. the
70
- `vec3`/`vec4`/`mat4x4` math) is one function per file, collected by the
71
- folder's `public.ts` barrel — dozens of one-line files is the intended shape,
72
- not a reason to lump.
73
-
74
- - **Barrels** — `index.ts` and a namespace's `public.ts` *only* re-export
75
- (`export … from "./…"`); they never declare. Many re-exports is their job.
76
- - **The eponymous namespace file** (`<type>.ts`) — the type alias plus
77
- `export * as <Type> from "./public.js"` (a type and its namespace value).
78
- - **The single-file pattern** (below) — one value export plus type-only
79
- exports, for a trivial namespace not worth a folder.
80
- - **`<format>-schema.ts`** — a borrowed data-format projection (plain
81
- `interface`s), next to its parser/emitter.
82
- - **Explicit authoring-cohesion files** — where two exports are genuinely one
83
- unit to author, e.g. a presentation's `render` plus a co-located fixture
84
- bundle (`unlocalized`). Rare; the pair must truly belong together.
85
-
86
- Everything else is one-per-file. In particular, **ECS facet folders
87
- (`components/`, `resources/`, `archetypes/`, `indexes/`, …) are not exempt**:
88
- one component / resource / archetype / index per file, collected by the
89
- folder's `index.ts` barrel. Collapsing several into one `index.ts` is exactly
90
- the anti-pattern this rule exists to prevent.
91
-
92
- Never `*-types.ts`. "Types" is a meta-word the `.ts` extension already
93
- implies; the name has to predict the contents. If you can't beat
94
- `-types`, the file isn't a real lump — split per type, or inline at use
95
- site.
96
-
97
- ## Casing: `PascalCase` for types, `camelCase` for everything else
98
-
99
- Capitalize a name **only** when it is a type, or a value that *represents a
100
- type*. Everything else — functions, values, methods — is `camelCase`.
101
-
102
- A `Schema` is a runtime type, so this extends to schema helpers:
103
-
104
- - A **schema value that is a type** is `PascalCase`: the namespace schemas
105
- `F32`, `Boolean`, `Guid`, `True` (each *is* a type's schema).
106
- - A **higher-order schema function that returns a new type-representing
107
- schema** is a runtime **type constructor** — the value-level analogue of
108
- `Readonly<T>` / `Partial<T>` — and is `PascalCase`: `Nullable(schema)`,
109
- `Tuple(item, n)`, `SuccessResultSchema(v)`, `ProgressiveResultSchema(v, e)`.
110
- - Everything else stays `camelCase`, even when it touches schemas: functions
111
- that **annotate the same type** (`withValidation`), **build from a spec**
112
- (`fromArchetype`, `fromObjectProperties`, `fromStructProperties`),
113
- **resolve/transform** (`getDynamicSchema`, `toVertexBufferLayout`), or hang
114
- off a namespace (`Guid.create`, `FractionalIndex.between`).
115
-
116
- The test: *does calling it yield a new runtime type?* Yes → `PascalCase`
117
- constructor. No → `camelCase`.
118
-
119
- ## Domain namespaces (for types you don't own)
120
-
121
- When utility functions operate on a platform or third-party type (e.g.
122
- `GPUDevice`, `GPUTexture`), use a **domain namespace** instead: a folder
123
- named after the concept, with a namespace export but *no type re-export*.
18
+ - Constants are **camelCase** (`flicksPerSecond`, not `FLICKS_PER_SECOND`) — strong team preference
19
+ - Each function/type gets its own file matching its name
124
20
 
125
- ```
126
- src/gpu/
127
- gpu.ts # `export * as GPU from "./public.js"` — no type alias
128
- public.ts # re-exports every public helper
129
- <helper>.ts # one declaration per file
130
- ```
21
+ ## Constant types
22
+
23
+ | Visibility | Definition |
24
+ | ---------- | ------------------------------- |
25
+ | private | Single-file only, not exported |
26
+ | exported | Sole export from eponymous file |
27
+ | public | Re-exported from public.ts |
28
+ | internal | Exported, not in public.ts |
29
+
30
+ ## Constraints
31
+
32
+ - `<type-name>/<type-name>.ts` is the only public import surface
33
+ - Export a single type alias: `export type <type-name> = <type>` (or `export type * from "./<type-name>-types.js"` when the type lives in a
34
+ separate file)
35
+ - Schema-based types: schema in `<type-name>-schema.ts`, named `schema` (not `<type-name>Schema`), derive with
36
+ `Schema.ToType<typeof schema>`
37
+ - Export namespace: `export * as <type-name> from "./public.js"`
38
+ - In public.ts, re-export every public constant file
39
+ - Filenames map deterministically to single export:
40
+ - `foo.ts` → `export type Foo = ...`
41
+ - `do-bar.ts` → `export function doBar() { }`
42
+ - DO NOT append other suffixes like `-type` etc.
43
+ - Function files: name by purpose only (e.g., `add.ts`, `sub.ts`). **Do not prefix** with the type name — the folder provides the namespace
44
+ context.
45
+
46
+ ## Anti-patterns (do not copy)
47
+
48
+ - **`export namespace <TypeName> { ... }`** — Use `export * as <TypeName> from "./public.js"` instead.
49
+ - **Prefixing function files with the type name** (e.g., `vec2-add.ts`) — Use `add.ts`; the folder provides context.
50
+ - **Importing from `public.js` or `-schema.js`** outside the type folder — Consumers must import only from `<type-name>/<type-name>.ts`.
51
+
52
+ ## Consumers
53
+
54
+ **Never:**
55
+
56
+ - Import from `public.js` outside the type namespace folder
57
+ - Import from `<type-name>-schema.js` outside the type namespace folder
58
+
59
+ **Do:**
60
+
61
+ - `import { Volume } from "../../types/volume/volume.js"`
62
+ - For schema: `import { PlayerMark } from "../../types/player-mark/player-mark.js"`, then `PlayerMark.schema`
63
+
64
+ ## Schema pattern example
131
65
 
132
66
  ```ts
133
- // gpu/gpu.ts
134
- export * as GPU from "./public.js";
67
+ // src/types/player-mark/player-mark-schema.ts
68
+ export const schema = { enum: ['X', 'O'] } as const;
69
+
70
+ // src/types/player-mark/public.ts
71
+ export { schema } from './player-mark-schema.js';
135
72
 
136
- // gpu/public.ts
137
- export { createCubemap } from "./create-cubemap.js";
138
- export { cubeFaceView } from "./cube-face-view.js";
73
+ // src/types/player-mark/player-mark.ts
74
+ import { Schema } from '@adobe/data/schema';
75
+ import { schema } from './player-mark-schema.js';
76
+ export type PlayerMark = Schema.ToType<typeof schema>;
77
+ export * as PlayerMark from './public.js';
139
78
  ```
140
79
 
141
- Consumers import the namespace and get discoverability without shadowing
142
- the platform type:
80
+ ## Example: `Point` type refactor
81
+
82
+ Starting file `src/types/point.ts`:
143
83
 
144
84
  ```ts
145
- import { GPU } from "@adobe/data-graphics";
146
- GPU.createCubemap(device, 256, "rgba16float");
85
+ export type Point = { x: number; y: number };
86
+ export const length = ({ x, y }: Point) => Math.hypot(x, y);
87
+ export const add = ({ x: x1, y: y1 }: Point, { x: x2, y: y2 }: Point): Point => ({ x: x1 + x2, y: y1 + y2 });
147
88
  ```
148
89
 
149
- **Name by purpose, not by the external type.** `GPU`, not `GPUDevice`.
90
+ After applying the namespace pattern:
91
+
92
+ ```ts
93
+ // src/types/point/point.ts
94
+ export type Point = { x: number; y: number };
95
+ export * as Point from './public.js';
96
+
97
+ // src/types/point/public.ts
98
+ export * from './length.js';
99
+ export * from './add.js';
100
+
101
+ // src/types/point/length.ts
102
+ export const length = ({ x, y }: Point) => Math.hypot(x, y);
103
+
104
+ // src/types/point/add.ts
105
+ export const add = ({ x: x1, y: y1 }: Point, { x: x2, y: y2 }: Point): Point => ({ x: x1 + x2, y: y1 + y2 });
106
+ ```
150
107
 
151
- **Tree-shaking still works**: bundlers (Rollup/Rolldown) trace static
152
- property accesses (`GPU.createCubemap`) and exclude unused helpers.
153
- Dynamic access (`GPU[name]`) defeats this — avoid it.
108
+ ## Execute
154
109
 
155
- **When NOT to use a domain namespace**: if a utility is only useful as
156
- an internal implementation detail of a specific plugin (not callable by
157
- consumers), keep it private to that plugin's folder instead.
110
+ 1. **Follow this rule** — Do not copy patterns from existing code; apply this standard.
111
+ 2. Identify constants in the current file.
112
+ 3. Extract them into the standard pattern.
113
+ 4. Identify all consumers of public namespaces.
114
+ 5. Check each consumer: ensure imports use `<type-name>.ts` only, never public.js or -schema.js.
115
+ 6. Ensure unit tests exist per function file, splitting as needed.
158
116
 
159
117
  ## Single file pattern
160
118