@adobe/data 0.9.56 → 0.9.58

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.
Files changed (180) hide show
  1. package/dist/cache/blob-store.js +12 -7
  2. package/dist/cache/blob-store.js.map +1 -1
  3. package/dist/ecs/database/database.d.ts +59 -2
  4. package/dist/ecs/database/database.js.map +1 -1
  5. package/dist/ecs/database/database.reset.test.js +200 -0
  6. package/dist/ecs/database/database.reset.test.js.map +1 -0
  7. package/dist/ecs/database/deep-extends-chain.type-test.d.ts +208 -208
  8. package/dist/ecs/database/observed/create-observed-database.js +4 -0
  9. package/dist/ecs/database/observed/create-observed-database.js.map +1 -1
  10. package/dist/ecs/database/observed/observed-database.d.ts +5 -3
  11. package/dist/ecs/database/public/create-database.d.ts +32 -3
  12. package/dist/ecs/database/public/create-database.js +12 -69
  13. package/dist/ecs/database/public/create-database.js.map +1 -1
  14. package/dist/ecs/database/public/create-database.test.js +86 -4
  15. package/dist/ecs/database/public/create-database.test.js.map +1 -1
  16. package/dist/ecs/database/public/create-transaction-dispatcher.d.ts +60 -0
  17. package/dist/ecs/database/public/create-transaction-dispatcher.js +119 -0
  18. package/dist/ecs/database/public/create-transaction-dispatcher.js.map +1 -0
  19. package/dist/ecs/database/reconciling/create-reconciling-database.js +52 -18
  20. package/dist/ecs/database/reconciling/create-reconciling-database.js.map +1 -1
  21. package/dist/ecs/database/reconciling/create-reconciling-database.test.js +144 -0
  22. package/dist/ecs/database/reconciling/create-reconciling-database.test.js.map +1 -1
  23. package/dist/ecs/database/reconciling/reconciling-database.d.ts +8 -1
  24. package/dist/ecs/database/reconciling/reconciling-entry.d.ts +9 -3
  25. package/dist/ecs/database/reconciling/reconciling-entry.js.map +1 -1
  26. package/dist/ecs/database/transactional-store/create-transactional-store.js +3 -1
  27. package/dist/ecs/database/transactional-store/create-transactional-store.js.map +1 -1
  28. package/dist/ecs/database/transactional-store/transactional-store.d.ts +14 -1
  29. package/dist/ecs/entity-location-table/create-entity-location-table.js +6 -1
  30. package/dist/ecs/entity-location-table/create-entity-location-table.js.map +1 -1
  31. package/dist/ecs/entity-location-table/entity-location-table.d.ts +2 -0
  32. package/dist/ecs/store/core/core.d.ts +2 -0
  33. package/dist/ecs/store/core/create-core.js +8 -0
  34. package/dist/ecs/store/core/create-core.js.map +1 -1
  35. package/dist/ecs/store/public/create-store.js +6 -0
  36. package/dist/ecs/store/public/create-store.js.map +1 -1
  37. package/dist/ecs/store/store.d.ts +2 -0
  38. package/dist/ecs/store/store.js.map +1 -1
  39. package/dist/ecs/store/transaction-functions.d.ts +36 -4
  40. package/dist/ecs/store/transaction-functions.type-test.js +50 -0
  41. package/dist/ecs/store/transaction-functions.type-test.js.map +1 -0
  42. package/dist/observe/to-async-generator.js +18 -8
  43. package/dist/observe/to-async-generator.js.map +1 -1
  44. package/dist/observe/to-async-generator.test.js +24 -0
  45. package/dist/observe/to-async-generator.test.js.map +1 -1
  46. package/dist/service/async-data-service/async-data-service.d.ts +1 -0
  47. package/dist/service/async-data-service/async-data-service.js +3 -0
  48. package/dist/service/async-data-service/async-data-service.js.map +1 -0
  49. package/dist/service/async-data-service/example.js +1 -1
  50. package/dist/service/async-data-service/example.js.map +1 -1
  51. package/dist/service/index.d.ts +2 -1
  52. package/dist/service/index.js +2 -1
  53. package/dist/service/index.js.map +1 -1
  54. package/dist/service/{agentic-service/link.js → ui-service/example.js} +1 -1
  55. package/dist/service/ui-service/example.js.map +1 -0
  56. package/dist/service/ui-service/from-service.d.ts +35 -0
  57. package/dist/service/{async-data-service/is-valid.type-test.js → ui-service/from-service.js} +1 -1
  58. package/dist/service/ui-service/from-service.js.map +1 -0
  59. package/dist/service/ui-service/is-valid.d.ts +11 -0
  60. package/dist/service/ui-service/is-valid.js +3 -0
  61. package/dist/service/ui-service/is-valid.js.map +1 -0
  62. package/dist/service/ui-service/public.d.ts +2 -0
  63. package/dist/service/ui-service/public.js +4 -0
  64. package/dist/service/ui-service/public.js.map +1 -0
  65. package/dist/service/ui-service/ui-service.d.ts +1 -0
  66. package/dist/service/ui-service/ui-service.js +3 -0
  67. package/dist/service/ui-service/ui-service.js.map +1 -0
  68. package/dist/tsconfig.tsbuildinfo +1 -1
  69. package/package.json +1 -1
  70. package/references/data-lit/package.json +1 -1
  71. package/references/data-lit/src/elements/application-element.ts +11 -1
  72. package/references/data-lit/src/elements/database-element.ts +3 -2
  73. package/references/data-lit/src/elements/database-element.type-test.ts +85 -0
  74. package/references/data-lit/src/hooks/index.ts +2 -0
  75. package/references/data-lit/src/hooks/use-drag-generator.ts +43 -0
  76. package/references/data-lit/src/hooks/use-drag-observe.ts +8 -2
  77. package/references/data-lit/src/hooks/use-pointer-observe.ts +79 -0
  78. package/references/data-lit-tictactoe/package.json +4 -1
  79. package/references/data-lit-tictactoe/src/elements/tictactoe-app/tictactoe-app-element.ts +23 -0
  80. package/references/data-lit-tictactoe/src/elements/tictactoe-app/tictactoe-app-presentation.ts +12 -0
  81. package/references/data-lit-tictactoe/src/elements/tictactoe-app/tictactoe-app.ts +14 -18
  82. package/references/data-lit-tictactoe/src/elements/tictactoe-board/tictactoe-board-element.ts +23 -0
  83. package/references/data-lit-tictactoe/src/elements/tictactoe-board/tictactoe-board-presentation.ts +6 -7
  84. package/references/data-lit-tictactoe/src/elements/tictactoe-board/tictactoe-board.ts +5 -15
  85. package/references/data-lit-tictactoe/src/elements/tictactoe-cell/tictactoe-cell-element.ts +45 -0
  86. package/references/data-lit-tictactoe/src/elements/tictactoe-cell/tictactoe-cell-presentation.ts +2 -1
  87. package/references/data-lit-tictactoe/src/elements/tictactoe-cell/tictactoe-cell.ts +5 -38
  88. package/references/data-lit-tictactoe/src/elements/tictactoe-hud/tictactoe-hud-element.ts +57 -0
  89. package/references/data-lit-tictactoe/src/elements/tictactoe-hud/tictactoe-hud-presentation.ts +10 -2
  90. package/references/data-lit-tictactoe/src/elements/tictactoe-hud/tictactoe-hud.css.ts +36 -1
  91. package/references/data-lit-tictactoe/src/elements/tictactoe-hud/tictactoe-hud.ts +5 -41
  92. package/references/data-lit-tictactoe/src/index.ts +16 -0
  93. package/references/data-lit-tictactoe/src/main.ts +9 -3
  94. package/references/data-lit-tictactoe/src/state/tictactoe-plugin.ts +18 -10
  95. package/references/data-lit-tictactoe/src/tictactoe-element.ts +8 -3
  96. package/references/data-lit-tictactoe/src/types/board-state/current-player.ts +2 -2
  97. package/references/data-lit-tictactoe/src/types/player-mark/is.ts +8 -0
  98. package/references/data-lit-tictactoe/src/types/player-mark/mark-color.ts +12 -0
  99. package/references/data-lit-tictactoe/src/types/player-mark/opponent.ts +12 -0
  100. package/references/data-lit-tictactoe/src/types/player-mark/public.ts +4 -0
  101. package/references/data-lit-tictactoe/src/types/player-mark/values.ts +6 -0
  102. package/references/data-react/package.json +1 -1
  103. package/references/data-react-hello/package.json +1 -1
  104. package/references/data-react-pixie/package.json +1 -1
  105. package/dist/cache/blob-store.test.js +0 -122
  106. package/dist/cache/blob-store.test.js.map +0 -1
  107. package/dist/cache/data-cache.test.js +0 -41
  108. package/dist/cache/data-cache.test.js.map +0 -1
  109. package/dist/cache/expiring-data-cache.test.js +0 -42
  110. package/dist/cache/expiring-data-cache.test.js.map +0 -1
  111. package/dist/cache/functions/functions.test.d.ts +0 -1
  112. package/dist/cache/functions/functions.test.js +0 -72
  113. package/dist/cache/functions/functions.test.js.map +0 -1
  114. package/dist/ecs/database/observe-select-deep.d.ts +0 -23
  115. package/dist/ecs/database/observe-select-deep.js +0 -15
  116. package/dist/ecs/database/observe-select-deep.js.map +0 -1
  117. package/dist/ecs/database/observe-select-deep.type-test.d.ts +0 -1
  118. package/dist/ecs/database/observe-select-deep.type-test.js +0 -111
  119. package/dist/ecs/database/observe-select-deep.type-test.js.map +0 -1
  120. package/dist/ecs/database/public/create-database-scheduler.test.d.ts +0 -1
  121. package/dist/ecs/database/public/create-database-scheduler.test.js +0 -67
  122. package/dist/ecs/database/public/create-database-scheduler.test.js.map +0 -1
  123. package/dist/ecs/database/public/create-database.type-test.d.ts +0 -1
  124. package/dist/ecs/database/public/create-database.type-test.js +0 -52
  125. package/dist/ecs/database/public/create-database.type-test.js.map +0 -1
  126. package/dist/ecs/entity.d.ts +0 -11
  127. package/dist/ecs/entity.js +0 -7
  128. package/dist/ecs/entity.js.map +0 -1
  129. package/dist/ecs/persistence-service/create-storage-persistence-service.test.d.ts +0 -1
  130. package/dist/ecs/persistence-service/create-storage-persistence-service.test.js +0 -52
  131. package/dist/ecs/persistence-service/create-storage-persistence-service.test.js.map +0 -1
  132. package/dist/functions/serialization/serialize-to-storage.test.d.ts +0 -1
  133. package/dist/functions/serialization/serialize-to-storage.test.js +0 -161
  134. package/dist/functions/serialization/serialize-to-storage.test.js.map +0 -1
  135. package/dist/math/aabb/face/aabb-face.test.d.ts +0 -1
  136. package/dist/math/aabb/face/aabb-face.test.js +0 -35
  137. package/dist/math/aabb/face/aabb-face.test.js.map +0 -1
  138. package/dist/math/aabb/face/functions.d.ts +0 -47
  139. package/dist/math/aabb/face/functions.js +0 -142
  140. package/dist/math/aabb/face/functions.js.map +0 -1
  141. package/dist/math/aabb/face/index.d.ts +0 -4
  142. package/dist/math/aabb/face/index.js +0 -3
  143. package/dist/math/aabb/face/index.js.map +0 -1
  144. package/dist/perftest/ecs-perf.d.ts +0 -49
  145. package/dist/perftest/ecs-perf.js +0 -210
  146. package/dist/perftest/ecs-perf.js.map +0 -1
  147. package/dist/service/agentic-service/link.d.ts +0 -15
  148. package/dist/service/agentic-service/link.js.map +0 -1
  149. package/dist/service/async-data-service/is-valid.type-test.d.ts +0 -1
  150. package/dist/service/async-data-service/is-valid.type-test.js.map +0 -1
  151. package/dist/service/dynamic-service/create-from-ecs.d.ts +0 -17
  152. package/dist/service/dynamic-service/create-from-ecs.js +0 -56
  153. package/dist/service/dynamic-service/create-from-ecs.js.map +0 -1
  154. package/dist/service/dynamic-service/create-from-ecs.test.d.ts +0 -1
  155. package/dist/service/dynamic-service/create-from-ecs.test.js +0 -636
  156. package/dist/service/dynamic-service/create-from-ecs.test.js.map +0 -1
  157. package/dist/service/dynamic-service/create.d.ts +0 -84
  158. package/dist/service/dynamic-service/create.js +0 -97
  159. package/dist/service/dynamic-service/create.js.map +0 -1
  160. package/dist/service/dynamic-service/create.test.d.ts +0 -1
  161. package/dist/service/dynamic-service/create.test.js +0 -688
  162. package/dist/service/dynamic-service/create.test.js.map +0 -1
  163. package/dist/service/dynamic-service/dynamic-service.d.ts +0 -29
  164. package/dist/service/dynamic-service/dynamic-service.js +0 -2
  165. package/dist/service/dynamic-service/dynamic-service.js.map +0 -1
  166. package/dist/service/dynamic-service/index.d.ts +0 -1
  167. package/dist/service/dynamic-service/index.js +0 -3
  168. package/dist/service/dynamic-service/index.js.map +0 -1
  169. package/dist/service/dynamic-service/public.d.ts +0 -2
  170. package/dist/service/dynamic-service/public.js +0 -3
  171. package/dist/service/dynamic-service/public.js.map +0 -1
  172. package/dist/service/dynamic-service/semantic-service.d.ts +0 -19
  173. package/dist/service/dynamic-service/semantic-service.js +0 -2
  174. package/dist/service/dynamic-service/semantic-service.js.map +0 -1
  175. package/dist/service/semantic-service/semantic-service.d.ts +0 -19
  176. package/dist/service/semantic-service/semantic-service.js +0 -2
  177. package/dist/service/semantic-service/semantic-service.js.map +0 -1
  178. /package/dist/{cache/blob-store.test.d.ts → ecs/database/database.reset.test.d.ts} +0 -0
  179. /package/dist/{cache/data-cache.test.d.ts → ecs/store/transaction-functions.type-test.d.ts} +0 -0
  180. /package/dist/{cache/expiring-data-cache.test.d.ts → service/ui-service/example.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/data",
3
- "version": "0.9.56",
3
+ "version": "0.9.58",
4
4
  "description": "Adobe data oriented programming library",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/data-lit",
3
- "version": "0.9.56",
3
+ "version": "0.9.58",
4
4
  "description": "Adobe data Lit bindings - hooks, elements, decorators",
5
5
  "type": "module",
6
6
  "private": false,
@@ -10,6 +10,15 @@ export class ApplicationElement<MainService extends Service> extends LitElement
10
10
  @property({ type: Object, reflect: false })
11
11
  service!: MainService;
12
12
 
13
+ // Defaults to `isService`: any Service in the ancestor chain is acceptable.
14
+ // Subclasses with a more specific MainService should override this with a
15
+ // narrower guard so unrelated services in the chain are skipped over. The
16
+ // cast is sound because callers that *don't* override are by definition
17
+ // accepting any Service as a valid MainService.
18
+ @property({ attribute: false })
19
+ typeGuard: (service: unknown) => service is MainService =
20
+ isService as (service: unknown) => service is MainService;
21
+
13
22
  constructor() {
14
23
  super();
15
24
  attachDecorator(this, 'render', withHooks);
@@ -27,9 +36,10 @@ export class ApplicationElement<MainService extends Service> extends LitElement
27
36
  }
28
37
 
29
38
  protected findAncestorService(): MainService | void {
39
+ const { typeGuard } = this;
30
40
  for (const element of iterateSelfAndAncestors(this)) {
31
41
  const { service } = element as Partial<ApplicationElement<MainService>>;
32
- if (isService(service)) {
42
+ if (typeGuard(service)) {
33
43
  return service;
34
44
  }
35
45
  }
@@ -4,12 +4,13 @@ import { LitElement } from 'lit';
4
4
  import { property } from 'lit/decorators.js';
5
5
  import { iterateSelfAndAncestors } from '../functions/index.js';
6
6
  import { Database } from '@adobe/data/ecs';
7
+ import { UIService } from '@adobe/data/service';
7
8
  import { attachDecorator, withHooks } from '../index.js';
8
9
 
9
10
  export abstract class DatabaseElement<P extends Database.Plugin> extends LitElement {
10
11
 
11
12
  @property({ type: Object, reflect: false })
12
- service!: Database.Plugin.ToDatabase<P>;
13
+ service!: UIService.FromService<Database.Plugin.ToDatabase<P>>;
13
14
 
14
15
  constructor() {
15
16
  super();
@@ -21,7 +22,7 @@ export abstract class DatabaseElement<P extends Database.Plugin> extends LitElem
21
22
  connectedCallback(): void {
22
23
  if (!this.service) {
23
24
  const service = this.findAncestorDatabase();
24
- this.service = (service?.extend(this.plugin) ?? Database.create(this.plugin)) as unknown as Database.Plugin.ToDatabase<P>;
25
+ this.service = (service?.extend(this.plugin) ?? Database.create(this.plugin)) as unknown as UIService.FromService<Database.Plugin.ToDatabase<P>>;
25
26
  }
26
27
  super.connectedCallback();
27
28
  }
@@ -0,0 +1,85 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+
3
+ /**
4
+ * Compile-time tests proving that `DatabaseElement.service` exposes a
5
+ * UI-safe view of the underlying Database — every Database method that
6
+ * originally returned a non-`void`, non-`Observe` result has its return
7
+ * type rewritten to `void`, while Observe surfaces pass through unchanged.
8
+ *
9
+ * If `UIService.FromService` ever stops applying to the inferred
10
+ * `service` type, one or more of these assertions will fail at build time.
11
+ */
12
+
13
+ /* eslint-disable @typescript-eslint/no-unused-vars */
14
+
15
+ import { Database } from "@adobe/data/ecs";
16
+ import { Observe } from "@adobe/data/observe";
17
+ import { UIService } from "@adobe/data/service";
18
+ import { DatabaseElement } from "./database-element.js";
19
+
20
+ const plugin = Database.Plugin.create({
21
+ resources: {
22
+ count: { default: 0 as number },
23
+ },
24
+ transactions: {
25
+ increment: (t) => {
26
+ t.resources.count = t.resources.count + 1;
27
+ },
28
+ },
29
+ });
30
+
31
+ class _CountElement extends DatabaseElement<typeof plugin> {
32
+ get plugin() {
33
+ return plugin;
34
+ }
35
+ }
36
+
37
+ type ServiceType = _CountElement["service"];
38
+ type RawDatabase = Database.Plugin.ToDatabase<typeof plugin>;
39
+
40
+ // 1. The exposed service type is the UIService-restricted view of the database.
41
+ type _CheckRestricted = Assert<Equal<ServiceType, UIService.FromService<RawDatabase>>>;
42
+
43
+ // 2. Observe surfaces survive the restriction.
44
+ type _CheckResourceObserveSurvives = Assert<
45
+ Equal<ServiceType["observe"]["resources"]["count"], Observe<number>>
46
+ >;
47
+ type _CheckEnvelopeObserveSurvives = Assert<
48
+ Equal<ServiceType["observe"]["envelopes"], RawDatabase["observe"]["envelopes"]>
49
+ >;
50
+
51
+ // 3. `apply` raw return type is non-void; restriction rewrites it to void.
52
+ type RawApply = RawDatabase["apply"];
53
+ type RestrictedApply = ServiceType["apply"];
54
+ type _CheckRawApplyIsNotVoid = Assert<NotEqual<ReturnType<RawApply>, void>>;
55
+ type _CheckRestrictedApplyIsVoid = Assert<Equal<ReturnType<RestrictedApply>, void>>;
56
+ type _CheckRestrictedApplyKeepsArgs = Assert<
57
+ Equal<Parameters<RestrictedApply>, Parameters<RawApply>>
58
+ >;
59
+
60
+ // 4. `toData` raw return is `unknown`; restriction rewrites to void.
61
+ type _CheckRawToDataNotVoid = Assert<NotEqual<ReturnType<RawDatabase["toData"]>, void>>;
62
+ type _CheckRestrictedToDataVoid = Assert<Equal<ReturnType<ServiceType["toData"]>, void>>;
63
+
64
+ // 5. Already-void functions pass through unchanged.
65
+ type _CheckResetStillVoid = Assert<Equal<ReturnType<ServiceType["reset"]>, void>>;
66
+ type _CheckCancelStillVoid = Assert<Equal<ReturnType<ServiceType["cancel"]>, void>>;
67
+
68
+ // 6. Transaction functions (nested under `transactions`) get their return
69
+ // rewritten to void by the same recursion. The plugin's `increment`
70
+ // happens to already be void, so we check the structure stays a function
71
+ // returning void after the restriction.
72
+ type _CheckIncrementReturnsVoid = Assert<
73
+ Equal<ReturnType<ServiceType["transactions"]["increment"]>, void>
74
+ >;
75
+
76
+ // ----------------------------------------------------------------------------
77
+ // Local helpers (kept inline so this file stays a self-contained type test).
78
+ // ----------------------------------------------------------------------------
79
+
80
+ type Assert<T extends true> = T;
81
+ type Equal<X, Y> =
82
+ (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2)
83
+ ? true
84
+ : false;
85
+ type NotEqual<X, Y> = Equal<X, Y> extends true ? false : true;
@@ -17,5 +17,7 @@ export * from "./with-hooks.js";
17
17
  export * from "./attach-decorator.js";
18
18
  export * from "./use-drag-transaction.js";
19
19
  export * from "./use-drag-observe.js";
20
+ export * from "./use-drag-generator.js";
20
21
  export * from "./use-draggable.js";
21
22
  export * from "./use-updated.js";
23
+ export * from "./use-pointer-observe.js";
@@ -0,0 +1,43 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+
3
+ import { Observe } from '@adobe/data/observe';
4
+ import { DragObserveProps, DragState, useDragObserve } from './use-drag-observe.js';
5
+ import { useEffect } from './use-effect.js';
6
+
7
+ /**
8
+ * When a drag starts, invokes `callback` with an async generator that yields
9
+ * {@link DragState} values until the drag ends or is cancelled. Built on
10
+ * {@link useDragObserve}.
11
+ *
12
+ * The callback also receives the `PointerEvent` that triggered `'start'` —
13
+ * i.e. the `pointermove` whose cumulative distance first exceeded
14
+ * `minDragDistance`. Callers that need the initial `clientX` / `clientY`
15
+ * synchronously (before any transaction runs or any Lit re-render has flushed)
16
+ * can use this event without awaiting the generator.
17
+ *
18
+ * The generator pairs naturally with an ECS transaction that accepts an
19
+ * `AsyncArgsProvider`: feed each yielded `DragState` into a transient envelope
20
+ * by returning the desired transaction args from the generator, and the ECS
21
+ * (and any attached sync transport) replicates each intermediate state to
22
+ * peers. The transaction commits when the drag ends and is cancelled when the
23
+ * drag is cancelled.
24
+ */
25
+ export function useDragGenerator(
26
+ props: DragObserveProps,
27
+ dependencies: unknown[],
28
+ callback: (drag: AsyncGenerator<DragState>, startEvent: PointerEvent) => void,
29
+ ) {
30
+ const dragObserve = useDragObserve(props, dependencies);
31
+
32
+ useEffect(() => {
33
+ return dragObserve(value => {
34
+ if (value.type === 'start') {
35
+ const generator = Observe.toAsyncGenerator(
36
+ dragObserve,
37
+ v => v.type === 'end' || v.type === 'cancel',
38
+ );
39
+ callback(generator, value.event!);
40
+ }
41
+ });
42
+ }, dependencies);
43
+ }
@@ -11,6 +11,12 @@ export type DragObserveProps = Pick<
11
11
  >;
12
12
  export type DragStart = {
13
13
  readonly type: 'start' | 'cancel';
14
+ /**
15
+ * The `PointerEvent` whose cumulative drag distance first exceeded
16
+ * `minDragDistance`. Only present when `type === 'start'`. Useful for
17
+ * callers that need the originating `clientX`/`clientY` synchronously.
18
+ */
19
+ readonly event?: PointerEvent;
14
20
  };
15
21
  export type DragMove = {
16
22
  readonly type: 'move';
@@ -36,8 +42,8 @@ export function useDragObserve(props: DragObserveProps, dependencies: unknown[])
36
42
  useDraggable(element,
37
43
  {
38
44
  ...props,
39
- onDragStart: _e => {
40
- setDragState({ type: 'start' });
45
+ onDragStart: e => {
46
+ setDragState({ type: 'start', event: e });
41
47
  },
42
48
  onDrag: (_e, position, delta) => {
43
49
  setDragState({
@@ -0,0 +1,79 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+
3
+ import { Observe } from "@adobe/data/observe";
4
+ import { Vec2 } from "@adobe/data/math";
5
+ import { useElement } from "./use-element.js";
6
+ import { useEffect } from "./use-effect.js";
7
+ import { useMemo } from "./use-memo.js";
8
+
9
+ /**
10
+ * Returns an `Observe<Vec2>` that fires on every `pointermove` over the host
11
+ * element. Coordinates are relative to the element's bounding box, in pixels.
12
+ *
13
+ * This is the primitive for **continuous** pointer tracking — presence
14
+ * indicators, live cursors, heat-maps, etc. It is intentionally simpler than
15
+ * `useDragObserve`: it emits without a drag start/end lifecycle.
16
+ *
17
+ * ### Driving a never-ending presence transaction
18
+ *
19
+ * Pair with `Observe.toAsyncGenerator` to feed a never-ending async-generator
20
+ * transaction. Each `yield` becomes a transient envelope that the sync
21
+ * service forwards as `kind: "transient"`. The reconciler's `(userId, id)`
22
+ * compound key replaces the previous sample, so each peer has at most one
23
+ * outstanding cursor sample at any time.
24
+ *
25
+ * ```ts
26
+ * const pointerPos = usePointerObserve([]);
27
+ *
28
+ * useEffect(() => {
29
+ * const positions = Observe.toAsyncGenerator(pointerPos, () => false);
30
+ *
31
+ * async function* presenceArgs() {
32
+ * for await (const [px, py] of positions) {
33
+ * const { width, height } = element.getBoundingClientRect();
34
+ * if (!width || !height) continue;
35
+ * yield { userId, x: px / width, y: py / height };
36
+ * }
37
+ * }
38
+ *
39
+ * // The wrapper returns a Promise when invoked with a generator factory.
40
+ * // It rejects when we `.throw()` on dispose; swallow that — we threw
41
+ * // precisely so the wrapper would cancel the in-flight transient
42
+ * // instead of promoting the last sample to a commit.
43
+ * db.transactions.movePresence(presenceArgs).catch(() => undefined);
44
+ *
45
+ * return () => {
46
+ * void positions.throw(new Error("disposed")).catch(() => undefined);
47
+ * };
48
+ * }, []);
49
+ * ```
50
+ *
51
+ * ### Why `() => false` as the `finished` predicate?
52
+ *
53
+ * `Observe.toAsyncGenerator` stops when `finished(value)` returns `true`.
54
+ * Passing `() => false` makes the generator run until `gen.throw()` (or
55
+ * `gen.return()`) is called from the effect cleanup — the correct lifetime
56
+ * for a presence stream.
57
+ */
58
+ export function usePointerObserve(dependencies: unknown[]): Observe<Vec2> {
59
+ // useMemo ensures the same [observe, notify] pair is reused across renders
60
+ // as long as dependencies are stable — critical so subscribers registered
61
+ // in a sibling useEffect don't miss events fired between renders.
62
+ const [pos, setPos] = useMemo(
63
+ () => Observe.createEvent<Vec2>(),
64
+ dependencies,
65
+ );
66
+
67
+ const element = useElement();
68
+
69
+ useEffect(() => {
70
+ const handler = (e: PointerEvent) => {
71
+ const rect = element.getBoundingClientRect();
72
+ setPos([e.clientX - rect.left, e.clientY - rect.top]);
73
+ };
74
+ element.addEventListener("pointermove", handler);
75
+ return () => element.removeEventListener("pointermove", handler);
76
+ }, [element, setPos]);
77
+
78
+ return pos;
79
+ }
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "data-lit-tictactoe",
3
- "version": "0.9.56",
3
+ "version": "0.9.58",
4
4
  "description": "Tic-Tac-Toe sample - Lit web components with @adobe/data-lit and AgenticService",
5
5
  "type": "module",
6
6
  "private": true,
7
+ "exports": {
8
+ ".": "./src/index.ts"
9
+ },
7
10
  "scripts": {
8
11
  "build": "vite build",
9
12
  "dev": "vite",
@@ -0,0 +1,23 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+
3
+ import { customElement } from "lit/decorators.js";
4
+ import { TictactoeElement } from "../../tictactoe-element.js";
5
+ import { styles } from "./tictactoe-app.css.js";
6
+ import * as presentation from "./tictactoe-app-presentation.js";
7
+
8
+ const tagName = "tictactoe-app";
9
+
10
+ declare global {
11
+ interface HTMLElementTagNameMap {
12
+ [tagName]: TictactoeAppElement;
13
+ }
14
+ }
15
+
16
+ @customElement(tagName)
17
+ export class TictactoeAppElement extends TictactoeElement {
18
+ static styles = styles;
19
+
20
+ render() {
21
+ return presentation.render();
22
+ }
23
+ }
@@ -0,0 +1,12 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+
3
+ import { html } from "lit";
4
+ import { TictactoeBoard } from "../tictactoe-board/tictactoe-board.js";
5
+ import { TictactoeHud } from "../tictactoe-hud/tictactoe-hud.js";
6
+
7
+ export function render() {
8
+ return html`
9
+ ${TictactoeBoard()}
10
+ ${TictactoeHud()}
11
+ `;
12
+ }
@@ -1,22 +1,18 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
 
3
- import { customElement } from "lit/decorators.js";
4
- import { html } from "lit";
5
- import { TictactoeElement } from "../../tictactoe-element.js";
6
- import { styles } from "./tictactoe-app.css.js";
7
- import "../tictactoe-board/tictactoe-board.js";
8
- import "../tictactoe-hud/tictactoe-hud.js";
3
+ import { html, type TemplateResult } from "lit";
4
+ import type { Database } from "@adobe/data/ecs";
5
+ import type { tictactoePlugin } from "../../state/tictactoe-plugin.js";
9
6
 
10
- export const tagName = "tictactoe-app";
7
+ type TictactoeService = Database.Plugin.ToDatabase<typeof tictactoePlugin>;
11
8
 
12
- @customElement(tagName)
13
- export class TictactoeApp extends TictactoeElement {
14
- static styles = styles;
15
-
16
- render() {
17
- return html`
18
- <tictactoe-board></tictactoe-board>
19
- <tictactoe-hud></tictactoe-hud>
20
- `;
21
- }
22
- }
9
+ /**
10
+ * Generic over `S` so callers may pass a database built from any plugin
11
+ * that extends `tictactoePlugin` (for example one that adds AI agents
12
+ * or peer presence). The element class itself is typed on the minimal
13
+ * `tictactoePlugin` surface and ignores the extra capabilities.
14
+ */
15
+ export const Tictactoe = <S extends TictactoeService>(args: { service: S }): TemplateResult => {
16
+ void import("./tictactoe-app-element.js");
17
+ return html`<tictactoe-app .service=${args.service}></tictactoe-app>`;
18
+ };
@@ -0,0 +1,23 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+
3
+ import { customElement } from "lit/decorators.js";
4
+ import { TictactoeElement } from "../../tictactoe-element.js";
5
+ import { styles } from "./tictactoe-board.css.js";
6
+ import * as presentation from "./tictactoe-board-presentation.js";
7
+
8
+ const tagName = "tictactoe-board";
9
+
10
+ declare global {
11
+ interface HTMLElementTagNameMap {
12
+ [tagName]: TictactoeBoardElement;
13
+ }
14
+ }
15
+
16
+ @customElement(tagName)
17
+ export class TictactoeBoardElement extends TictactoeElement {
18
+ static styles = styles;
19
+
20
+ render() {
21
+ return presentation.render();
22
+ }
23
+ }
@@ -1,13 +1,12 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
 
3
3
  import { html } from "lit";
4
+ import { TictactoeCell } from "../tictactoe-cell/tictactoe-cell.js";
4
5
 
5
6
  export function render() {
6
- return html`
7
- <div class="board">
8
- ${[0, 1, 2, 3, 4, 5, 6, 7, 8].map(
9
- (index) => html`<tictactoe-cell .index=${index}></tictactoe-cell>`,
10
- )}
11
- </div>
12
- `;
7
+ return html`
8
+ <div class="board">
9
+ ${[0, 1, 2, 3, 4, 5, 6, 7, 8].map((index) => TictactoeCell({ index }))}
10
+ </div>
11
+ `;
13
12
  }
@@ -1,18 +1,8 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
 
3
- import { customElement } from "lit/decorators.js";
4
- import { TictactoeElement } from "../../tictactoe-element.js";
5
- import { styles } from "./tictactoe-board.css.js";
6
- import * as presentation from "./tictactoe-board-presentation.js";
7
- import "../tictactoe-cell/tictactoe-cell.js";
3
+ import { html, type TemplateResult } from "lit";
8
4
 
9
- export const tagName = "tictactoe-board";
10
-
11
- @customElement(tagName)
12
- export class TictactoeBoard extends TictactoeElement {
13
- static styles = styles;
14
-
15
- render() {
16
- return presentation.render();
17
- }
18
- }
5
+ export const TictactoeBoard = (): TemplateResult => {
6
+ void import("./tictactoe-board-element.js");
7
+ return html`<tictactoe-board></tictactoe-board>`;
8
+ };
@@ -0,0 +1,45 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+
3
+ import { customElement, property } from "lit/decorators.js";
4
+ import { useObservableValues } from "@adobe/data-lit";
5
+ import { BoardState } from "../../types/board-state/board-state.js";
6
+ import { TictactoeElement } from "../../tictactoe-element.js";
7
+ import { styles } from "./tictactoe-cell.css.js";
8
+ import * as presentation from "./tictactoe-cell-presentation.js";
9
+
10
+ const tagName = "tictactoe-cell";
11
+
12
+ declare global {
13
+ interface HTMLElementTagNameMap {
14
+ [tagName]: TictactoeCellElement;
15
+ }
16
+ }
17
+
18
+ @customElement(tagName)
19
+ export class TictactoeCellElement extends TictactoeElement {
20
+ static styles = styles;
21
+
22
+ @property({ type: Number })
23
+ declare index: number;
24
+
25
+ render() {
26
+ const values = useObservableValues(
27
+ () => ({ board: this.service.observe.resources.board }),
28
+ [],
29
+ );
30
+
31
+ const board = values?.board ?? " ";
32
+ const cell = BoardState.getCell(board, this.index);
33
+ const isWinning = BoardState.isCellWinning(board, this.index);
34
+ const isPlayable = BoardState.isCellPlayable(board, this.index);
35
+
36
+ return presentation.render({
37
+ cell,
38
+ isWinning,
39
+ isPlayable,
40
+ playMove: () => {
41
+ this.service.transactions.playMove({ index: this.index });
42
+ },
43
+ });
44
+ }
45
+ }
@@ -1,6 +1,7 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
 
3
3
  import { html } from "lit";
4
+ import { PlayerMark } from "../../types/player-mark/player-mark.js";
4
5
 
5
6
  export function render(args: {
6
7
  cell: string;
@@ -9,7 +10,7 @@ export function render(args: {
9
10
  playMove: () => void;
10
11
  }) {
11
12
  const { cell, isWinning, isPlayable, playMove } = args;
12
- const hasMark = cell === "X" || cell === "O";
13
+ const hasMark = PlayerMark.is(cell);
13
14
 
14
15
  return html`
15
16
  <div
@@ -1,41 +1,8 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
 
3
- import { customElement, property } from "lit/decorators.js";
4
- import { useObservableValues } from "@adobe/data-lit";
5
- import { BoardState } from "../../types/board-state/board-state.js";
6
- import { TictactoeElement } from "../../tictactoe-element.js";
7
- import { styles } from "./tictactoe-cell.css.js";
8
- import * as presentation from "./tictactoe-cell-presentation.js";
3
+ import { html, type TemplateResult } from "lit";
9
4
 
10
- export const tagName = "tictactoe-cell";
11
-
12
- @customElement(tagName)
13
- export class TictactoeCell extends TictactoeElement {
14
- static styles = styles;
15
-
16
- @property({ type: Number })
17
- declare index: number;
18
-
19
- render() {
20
- const values = useObservableValues(
21
- () => ({
22
- board: this.service.observe.resources.board,
23
- }),
24
- [],
25
- );
26
-
27
- const board = values?.board ?? " ";
28
- const cell = BoardState.getCell(board, this.index);
29
- const isWinning = BoardState.isCellWinning(board, this.index);
30
- const isPlayable = BoardState.isCellPlayable(board, this.index);
31
-
32
- return presentation.render({
33
- cell,
34
- isWinning,
35
- isPlayable,
36
- playMove: () => {
37
- this.service.transactions.playMove({ index: this.index });
38
- },
39
- });
40
- }
41
- }
5
+ export const TictactoeCell = (args: { index: number }): TemplateResult => {
6
+ void import("./tictactoe-cell-element.js");
7
+ return html`<tictactoe-cell .index=${args.index}></tictactoe-cell>`;
8
+ };
@@ -0,0 +1,57 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+
3
+ import { customElement } from "lit/decorators.js";
4
+ import { useObservableValues } from "@adobe/data-lit";
5
+ import { BoardState } from "../../types/board-state/board-state.js";
6
+ import { PlayerMark } from "../../types/player-mark/player-mark.js";
7
+ import { TictactoeElement } from "../../tictactoe-element.js";
8
+ import { styles } from "./tictactoe-hud.css.js";
9
+ import * as presentation from "./tictactoe-hud-presentation.js";
10
+
11
+ const tagName = "tictactoe-hud";
12
+
13
+ declare global {
14
+ interface HTMLElementTagNameMap {
15
+ [tagName]: TictactoeHudElement;
16
+ }
17
+ }
18
+
19
+ @customElement(tagName)
20
+ export class TictactoeHudElement extends TictactoeElement {
21
+ static styles = styles;
22
+
23
+ render() {
24
+ const values = useObservableValues(
25
+ () => ({
26
+ board: this.service.observe.resources.board,
27
+ firstPlayer: this.service.observe.resources.firstPlayer,
28
+ xWins: this.service.observe.resources.xWins,
29
+ oWins: this.service.observe.resources.oWins,
30
+ draws: this.service.observe.resources.draws,
31
+ }),
32
+ [],
33
+ );
34
+
35
+ const board = values?.board ?? " ";
36
+ const firstPlayer = values?.firstPlayer ?? PlayerMark.values[0];
37
+
38
+ const currentPlayer = BoardState.currentPlayer(board, firstPlayer);
39
+ const status = BoardState.deriveStatus(board);
40
+ const winner = BoardState.getWinner(board);
41
+
42
+ const statusText =
43
+ status === "won" && winner !== null
44
+ ? `${winner} wins!`
45
+ : status === "draw"
46
+ ? "Draw!"
47
+ : `${currentPlayer}'s turn`;
48
+
49
+ return presentation.render({
50
+ statusText,
51
+ xWins: values?.xWins ?? 0,
52
+ oWins: values?.oWins ?? 0,
53
+ draws: values?.draws ?? 0,
54
+ restartGame: () => this.service.transactions.restartGame(),
55
+ });
56
+ }
57
+ }