@toyz/loom 0.3.0 → 0.5.0

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 (119) hide show
  1. package/dist/app.d.ts +4 -1
  2. package/dist/app.d.ts.map +1 -1
  3. package/dist/app.js +9 -9
  4. package/dist/app.js.map +1 -1
  5. package/dist/decorators/create.d.ts +16 -25
  6. package/dist/decorators/create.d.ts.map +1 -1
  7. package/dist/decorators/create.js +25 -34
  8. package/dist/decorators/create.js.map +1 -1
  9. package/dist/decorators/events.d.ts +10 -8
  10. package/dist/decorators/events.d.ts.map +1 -1
  11. package/dist/decorators/events.js +53 -43
  12. package/dist/decorators/events.js.map +1 -1
  13. package/dist/decorators/symbols.d.ts +4 -0
  14. package/dist/decorators/symbols.d.ts.map +1 -1
  15. package/dist/decorators/symbols.js +4 -0
  16. package/dist/decorators/symbols.js.map +1 -1
  17. package/dist/di/decorators.d.ts +15 -15
  18. package/dist/di/decorators.d.ts.map +1 -1
  19. package/dist/di/decorators.js +23 -33
  20. package/dist/di/decorators.js.map +1 -1
  21. package/dist/di/watch.d.ts +1 -7
  22. package/dist/di/watch.d.ts.map +1 -1
  23. package/dist/di/watch.js +17 -20
  24. package/dist/di/watch.js.map +1 -1
  25. package/dist/element/decorators.d.ts +26 -10
  26. package/dist/element/decorators.d.ts.map +1 -1
  27. package/dist/element/decorators.js +75 -31
  28. package/dist/element/decorators.js.map +1 -1
  29. package/dist/element/element.d.ts.map +1 -1
  30. package/dist/element/element.js +15 -3
  31. package/dist/element/element.js.map +1 -1
  32. package/dist/element/form.d.ts +63 -0
  33. package/dist/element/form.d.ts.map +1 -0
  34. package/dist/element/form.js +167 -0
  35. package/dist/element/form.js.map +1 -0
  36. package/dist/element/icon.d.ts +3 -3
  37. package/dist/element/icon.d.ts.map +1 -1
  38. package/dist/element/icon.js +131 -74
  39. package/dist/element/icon.js.map +1 -1
  40. package/dist/element/index.d.ts +8 -1
  41. package/dist/element/index.d.ts.map +1 -1
  42. package/dist/element/index.js +9 -1
  43. package/dist/element/index.js.map +1 -1
  44. package/dist/element/lazy.d.ts +31 -0
  45. package/dist/element/lazy.d.ts.map +1 -0
  46. package/dist/element/lazy.js +71 -0
  47. package/dist/element/lazy.js.map +1 -0
  48. package/dist/element/lifecycle.d.ts +27 -29
  49. package/dist/element/lifecycle.d.ts.map +1 -1
  50. package/dist/element/lifecycle.js +60 -79
  51. package/dist/element/lifecycle.js.map +1 -1
  52. package/dist/element/slots.d.ts +23 -0
  53. package/dist/element/slots.d.ts.map +1 -0
  54. package/dist/element/slots.js +54 -0
  55. package/dist/element/slots.js.map +1 -0
  56. package/dist/element/timing.d.ts +10 -7
  57. package/dist/element/timing.d.ts.map +1 -1
  58. package/dist/element/timing.js +67 -52
  59. package/dist/element/timing.js.map +1 -1
  60. package/dist/element/transition.d.ts +32 -0
  61. package/dist/element/transition.d.ts.map +1 -0
  62. package/dist/element/transition.js +70 -0
  63. package/dist/element/transition.js.map +1 -0
  64. package/dist/element/virtual.js +235 -194
  65. package/dist/element/virtual.js.map +1 -1
  66. package/dist/index.d.ts +4 -4
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +3 -3
  69. package/dist/index.js.map +1 -1
  70. package/dist/router/decorators.d.ts +9 -38
  71. package/dist/router/decorators.d.ts.map +1 -1
  72. package/dist/router/decorators.js +7 -52
  73. package/dist/router/decorators.js.map +1 -1
  74. package/dist/router/index.d.ts +1 -0
  75. package/dist/router/index.d.ts.map +1 -1
  76. package/dist/router/index.js +2 -0
  77. package/dist/router/index.js.map +1 -1
  78. package/dist/router/link.d.ts +4 -4
  79. package/dist/router/link.d.ts.map +1 -1
  80. package/dist/router/link.js +134 -72
  81. package/dist/router/link.js.map +1 -1
  82. package/dist/router/outlet.d.ts +2 -2
  83. package/dist/router/outlet.d.ts.map +1 -1
  84. package/dist/router/outlet.js +188 -138
  85. package/dist/router/outlet.js.map +1 -1
  86. package/dist/router/route-lifecycle.d.ts +28 -0
  87. package/dist/router/route-lifecycle.d.ts.map +1 -0
  88. package/dist/router/route-lifecycle.js +47 -0
  89. package/dist/router/route-lifecycle.js.map +1 -0
  90. package/dist/router/route.d.ts +1 -1
  91. package/dist/router/route.d.ts.map +1 -1
  92. package/dist/router/router.d.ts +5 -0
  93. package/dist/router/router.d.ts.map +1 -1
  94. package/dist/router/router.js +41 -4
  95. package/dist/router/router.js.map +1 -1
  96. package/dist/store/decorators.d.ts +39 -15
  97. package/dist/store/decorators.d.ts.map +1 -1
  98. package/dist/store/decorators.js +167 -79
  99. package/dist/store/decorators.js.map +1 -1
  100. package/dist/store/index.d.ts +1 -1
  101. package/dist/store/index.d.ts.map +1 -1
  102. package/dist/store/index.js +1 -1
  103. package/dist/store/index.js.map +1 -1
  104. package/dist/store/watch.d.ts +12 -11
  105. package/dist/store/watch.d.ts.map +1 -1
  106. package/dist/store/watch.js +20 -15
  107. package/dist/store/watch.js.map +1 -1
  108. package/dist/testing.d.ts +55 -0
  109. package/dist/testing.d.ts.map +1 -0
  110. package/dist/testing.js +99 -0
  111. package/dist/testing.js.map +1 -0
  112. package/dist/transform/transform.d.ts +7 -3
  113. package/dist/transform/transform.d.ts.map +1 -1
  114. package/dist/transform/transform.js +17 -9
  115. package/dist/transform/transform.js.map +1 -1
  116. package/dist/transform/typed.d.ts +1 -1
  117. package/dist/transform/typed.d.ts.map +1 -1
  118. package/dist/transform/typed.js.map +1 -1
  119. package/package.json +5 -1
@@ -1,41 +1,55 @@
1
1
  /**
2
- * Loom — Store decorators
2
+ * Loom — Store decorators (TC39 Stage 3)
3
3
  *
4
- * @reactive — Internal reactive state backed by Reactive<T>
5
- * @prop — External attribute with optional route binding
4
+ * @reactive — Internal reactive state backed by Reactive<T> (auto-accessor)
5
+ * @prop — External attribute with optional route binding (auto-accessor)
6
6
  * @computed — Cached derived getter
7
+ * @store — Component-scoped reactive store (auto-accessor)
7
8
  */
8
- /** Sentinel for full route-param decompose: `@prop({params}) p!: MyType` */
9
+ import type { PersistOptions } from "./storage";
10
+ /**
11
+ * Staging area for @prop registrations.
12
+ * TC39 member decorators evaluate before class decorators,
13
+ * so @prop pushes here and @component flushes it.
14
+ */
15
+ export declare const pendingProps: Array<{
16
+ key: string;
17
+ }>;
18
+ /** Sentinel for full route-param decompose: `@prop({params}) accessor p!: MyType` */
9
19
  export declare const params: unique symbol;
10
- /** Sentinel for full query-param decompose: `@prop({query}) q!: MyType` */
20
+ /** Sentinel for full query-param decompose: `@prop({query}) accessor q!: MyType` */
11
21
  export declare const routeQuery: unique symbol;
12
22
  /**
13
- * Internal reactive state. Creates a getter/setter backed by Reactive<T>.
23
+ * Internal reactive state. Auto-accessor backed by Reactive<T>.
14
24
  * Changes schedule batched `update()` via microtask.
15
25
  *
16
26
  * ```ts
17
- * @reactive count = 0;
27
+ * @reactive accessor count = 0;
18
28
  * ```
19
29
  */
20
- export declare function reactive(target: any, key: string): void;
30
+ export declare function reactive<This extends object, V>(target: ClassAccessorDecoratorTarget<This, V>, context: ClassAccessorDecoratorContext<This, V>): ClassAccessorDecoratorResult<This, V>;
31
+ type PropRouteOpts = {
32
+ param?: string;
33
+ params?: symbol;
34
+ query?: string | symbol;
35
+ };
21
36
  /**
22
37
  * External attribute. Observed HTML attribute that auto-parses from strings.
23
38
  * Uses @reactive under the hood.
24
39
  *
25
40
  * Bare decorator:
26
41
  * ```ts
27
- * @prop label = "Count"; // <my-counter label="Clicks">
42
+ * @prop accessor label = "Count";
28
43
  * ```
29
44
  *
30
45
  * Route param injection:
31
46
  * ```ts
32
- * @prop({ param: "id" }) userId!: string; // single param
33
- * @prop({params}) params!: MyParamType; // full decompose
34
- * @prop({ query: "tab" }) activeTab!: string; // single query param
35
- * @prop({query: routeQuery}) query!: MyQuery; // full query decompose
47
+ * @prop({ param: "id" }) accessor userId!: string;
48
+ * @prop({params}) accessor params!: MyParamType;
36
49
  * ```
37
50
  */
38
- export declare function prop(targetOrOpts: any, key?: any): any;
51
+ export declare function prop<This extends object, V>(target: ClassAccessorDecoratorTarget<This, V>, context: ClassAccessorDecoratorContext<This, V>): ClassAccessorDecoratorResult<This, V>;
52
+ export declare function prop(opts: PropRouteOpts): <This extends object, V>(target: ClassAccessorDecoratorTarget<This, V>, context: ClassAccessorDecoratorContext<This, V>) => ClassAccessorDecoratorResult<This, V>;
39
53
  /**
40
54
  * Cached derived value. Re-computed only when reactive dependencies fire.
41
55
  *
@@ -44,5 +58,15 @@ export declare function prop(targetOrOpts: any, key?: any): any;
44
58
  * get displayName() { return `${this.firstName} ${this.lastName}`; }
45
59
  * ```
46
60
  */
47
- export declare function computed(target: any, key: string, desc: PropertyDescriptor): void;
61
+ export declare function computed<This extends object, V>(target: (this: This) => V, context: ClassGetterDecoratorContext<This, V>): (this: This) => V;
62
+ /**
63
+ * Component-scoped reactive store with optional persistence (auto-accessor).
64
+ *
65
+ * ```ts
66
+ * @store<TodoState>({ items: [], filter: "all" })
67
+ * accessor state!: TodoState;
68
+ * ```
69
+ */
70
+ export declare function store<T extends object>(defaults: T, persist?: PersistOptions): <This extends object>(_target: ClassAccessorDecoratorTarget<This, T>, context: ClassAccessorDecoratorContext<This, T>) => ClassAccessorDecoratorResult<This, T>;
71
+ export {};
48
72
  //# sourceMappingURL=decorators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../../src/store/decorators.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,4EAA4E;AAC5E,eAAO,MAAM,MAAM,eAAiC,CAAC;AAErD,2EAA2E;AAG3E,eAAO,MAAM,UAAU,eAAgC,CAAC;AAExD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAsCvD;AAiBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,IAAI,CAClB,YAAY,EAAE,GAAG,EACjB,GAAG,CAAC,EAAE,GAAG,GACR,GAAG,CAuBL;AASD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,GAAG,EACX,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,kBAAkB,GACvB,IAAI,CAiBN"}
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../../src/store/decorators.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAM,CAAC;AAIvD,qFAAqF;AACrF,eAAO,MAAM,MAAM,eAAiC,CAAC;AAErD,oFAAoF;AACpF,eAAO,MAAM,UAAU,eAAgC,CAAC;AAExD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,IAAI,SAAS,MAAM,EAAE,CAAC,EAC7C,MAAM,EAAE,4BAA4B,CAAC,IAAI,EAAE,CAAC,CAAC,EAC7C,OAAO,EAAE,6BAA6B,CAAC,IAAI,EAAE,CAAC,CAAC,GAC9C,4BAA4B,CAAC,IAAI,EAAE,CAAC,CAAC,CAoDvC;AAWD,KAAK,aAAa,GAAG;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,IAAI,CAAC,IAAI,SAAS,MAAM,EAAE,CAAC,EACzC,MAAM,EAAE,4BAA4B,CAAC,IAAI,EAAE,CAAC,CAAC,EAC7C,OAAO,EAAE,6BAA6B,CAAC,IAAI,EAAE,CAAC,CAAC,GAC9C,4BAA4B,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AACzC,wBAAgB,IAAI,CAAC,IAAI,EAAE,aAAa,GAAG,CAAC,IAAI,SAAS,MAAM,EAAE,CAAC,EAChE,MAAM,EAAE,4BAA4B,CAAC,IAAI,EAAE,CAAC,CAAC,EAC7C,OAAO,EAAE,6BAA6B,CAAC,IAAI,EAAE,CAAC,CAAC,KAC5C,4BAA4B,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AA+C3C;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,IAAI,SAAS,MAAM,EAAE,CAAC,EAC7C,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,EACzB,OAAO,EAAE,2BAA2B,CAAC,IAAI,EAAE,CAAC,CAAC,GAC5C,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,CAsBnB;AA2DD;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,EACpC,QAAQ,EAAE,CAAC,EACX,OAAO,CAAC,EAAE,cAAc,IAEhB,IAAI,SAAS,MAAM,EACzB,SAAS,4BAA4B,CAAC,IAAI,EAAE,CAAC,CAAC,EAC9C,SAAS,6BAA6B,CAAC,IAAI,EAAE,CAAC,CAAC,KAC9C,4BAA4B,CAAC,IAAI,EAAE,CAAC,CAAC,CAgCzC"}
@@ -1,111 +1,111 @@
1
1
  /**
2
- * Loom — Store decorators
2
+ * Loom — Store decorators (TC39 Stage 3)
3
3
  *
4
- * @reactive — Internal reactive state backed by Reactive<T>
5
- * @prop — External attribute with optional route binding
4
+ * @reactive — Internal reactive state backed by Reactive<T> (auto-accessor)
5
+ * @prop — External attribute with optional route binding (auto-accessor)
6
6
  * @computed — Cached derived getter
7
+ * @store — Component-scoped reactive store (auto-accessor)
7
8
  */
8
- import { REACTIVES, PROPS, WATCHERS, EMITTERS, COMPUTED_DIRTY, ROUTE_PROPS } from "../decorators/symbols";
9
+ import { REACTIVES, WATCHERS, EMITTERS, COMPUTED_DIRTY, ROUTE_PROPS } from "../decorators/symbols";
9
10
  import { Reactive } from "./reactive";
10
11
  import { bus } from "../bus";
12
+ /**
13
+ * Staging area for @prop registrations.
14
+ * TC39 member decorators evaluate before class decorators,
15
+ * so @prop pushes here and @component flushes it.
16
+ */
17
+ export const pendingProps = [];
11
18
  // ── Route sentinels ──
12
- // Used with @prop shorthand: @prop({params}) or @prop({query})
13
- /** Sentinel for full route-param decompose: `@prop({params}) p!: MyType` */
19
+ /** Sentinel for full route-param decompose: `@prop({params}) accessor p!: MyType` */
14
20
  export const params = Symbol("loom:sentinel:params");
15
- /** Sentinel for full query-param decompose: `@prop({query}) q!: MyType` */
16
- // NOTE: This is NOT the @query(".selector") DOM decorator — that's in element/decorators.
17
- // This sentinel is re-exported from "@toyz/loom/router".
21
+ /** Sentinel for full query-param decompose: `@prop({query}) accessor q!: MyType` */
18
22
  export const routeQuery = Symbol("loom:sentinel:query");
19
23
  /**
20
- * Internal reactive state. Creates a getter/setter backed by Reactive<T>.
24
+ * Internal reactive state. Auto-accessor backed by Reactive<T>.
21
25
  * Changes schedule batched `update()` via microtask.
22
26
  *
23
27
  * ```ts
24
- * @reactive count = 0;
28
+ * @reactive accessor count = 0;
25
29
  * ```
26
30
  */
27
- export function reactive(target, key) {
28
- // Define-time: store field name and create getter/setter
29
- if (!target[REACTIVES])
30
- target[REACTIVES] = [];
31
- target[REACTIVES].push(key);
31
+ export function reactive(target, context) {
32
+ const key = String(context.name);
32
33
  const storageKey = Symbol(key);
33
- Object.defineProperty(target, key, {
34
+ // Store field name for LoomElement introspection
35
+ context.addInitializer(function () {
36
+ if (!this.constructor[REACTIVES])
37
+ this.constructor[REACTIVES] = [];
38
+ if (!this.constructor[REACTIVES].includes(key)) {
39
+ this.constructor[REACTIVES].push(key);
40
+ }
41
+ });
42
+ return {
34
43
  get() {
35
- return this[storageKey]?.value;
44
+ // Before first explicit set, fall back to the backing storage value from init()
45
+ if (!this[storageKey])
46
+ return target.get.call(this);
47
+ return this[storageKey].value;
36
48
  },
37
49
  set(val) {
38
50
  if (!this[storageKey]) {
39
- // First set (from field initializer)
40
- const r = new Reactive(val);
51
+ // First explicit set create Reactive with the init'd backing value,
52
+ // wire all subscribers, then set the new value to fire them.
53
+ const backingValue = target.get.call(this);
54
+ const r = new Reactive(backingValue);
41
55
  this[storageKey] = r;
42
56
  r.subscribe(() => this.scheduleUpdate?.());
43
- // Wire @watch handlers for this field
57
+ // Wire @watch handlers (WATCHERS is populated because method
58
+ // addInitializer runs BEFORE accessor field init in TC39)
44
59
  for (const w of (this[WATCHERS] ?? []).filter((w) => w.field === key)) {
45
60
  r.subscribe((v, prev) => this[w.key](v, prev));
46
61
  }
47
- // Wire @emit handlers for this field
62
+ // Wire @emit handlers
48
63
  for (const e of (this[EMITTERS] ?? []).filter((e) => e.field === key)) {
49
64
  r.subscribe((v) => bus.emit(e.factory(v)));
50
65
  }
66
+ // Now set the new value — this fires all subscribers
67
+ r.set(val);
51
68
  }
52
69
  else {
53
70
  this[storageKey].set(val);
54
71
  }
55
72
  },
56
- enumerable: true,
57
- configurable: true,
58
- });
73
+ init(val) {
74
+ return val;
75
+ },
76
+ };
59
77
  }
60
- /**
61
- * External attribute. Observed HTML attribute that auto-parses from strings.
62
- * Uses @reactive under the hood.
63
- *
64
- * Bare decorator:
65
- * ```ts
66
- * @prop label = "Count"; // <my-counter label="Clicks">
67
- * ```
68
- *
69
- * Route param injection:
70
- * ```ts
71
- * @prop({ param: "id" }) userId!: string; // single param
72
- * @prop({params}) params!: MyParamType; // full decompose
73
- * @prop({ query: "tab" }) activeTab!: string; // single query param
74
- * @prop({query: routeQuery}) query!: MyQuery; // full query decompose
75
- * ```
76
- */
77
- export function prop(targetOrOpts, key) {
78
- if (typeof key === "string") {
79
- // Bare @prop — existing behavior
80
- _registerProp(targetOrOpts, key);
81
- return;
78
+ export function prop(targetOrOpts, context) {
79
+ // Bare @prop auto-accessor decorator applied directly
80
+ if (context) {
81
+ const key = String(context.name);
82
+ const result = reactive(targetOrOpts, context);
83
+ // Stage for @component to flush at class-decoration time
84
+ pendingProps.push({ key });
85
+ return result;
82
86
  }
83
- // @prop({ param: "id" }) or @prop({params}) etc.
87
+ // @prop({ param: "id" }) returns decorator factory
84
88
  const opts = targetOrOpts;
85
- return (target, propKey) => {
86
- // Store route binding metadata on the constructor
87
- const ctor = target.constructor;
88
- if (!ctor[ROUTE_PROPS])
89
- ctor[ROUTE_PROPS] = [];
90
- const binding = { propKey };
91
- if (opts.params)
92
- binding.params = opts.params;
93
- if (opts.param)
94
- binding.param = opts.param;
95
- if (opts.query)
96
- binding.query = opts.query;
97
- ctor[ROUTE_PROPS].push(binding);
98
- // Wire @reactive so changes trigger re-renders
99
- reactive(target, propKey);
89
+ return (target, ctx) => {
90
+ const propKey = String(ctx.name);
91
+ const result = reactive(target, ctx);
92
+ // Store route binding metadata
93
+ ctx.addInitializer(function () {
94
+ const ctor = this.constructor;
95
+ if (!ctor[ROUTE_PROPS])
96
+ ctor[ROUTE_PROPS] = [];
97
+ const binding = { propKey };
98
+ if (opts.params)
99
+ binding.params = opts.params;
100
+ if (opts.param)
101
+ binding.param = opts.param;
102
+ if (opts.query)
103
+ binding.query = opts.query;
104
+ ctor[ROUTE_PROPS].push(binding);
105
+ });
106
+ return result;
100
107
  };
101
108
  }
102
- /** Register a bare @prop (attribute-observed reactive) */
103
- function _registerProp(target, key) {
104
- if (!target.constructor[PROPS])
105
- target.constructor[PROPS] = new Map();
106
- target.constructor[PROPS].set(key.toLowerCase(), key);
107
- reactive(target, key);
108
- }
109
109
  /**
110
110
  * Cached derived value. Re-computed only when reactive dependencies fire.
111
111
  *
@@ -114,21 +114,109 @@ function _registerProp(target, key) {
114
114
  * get displayName() { return `${this.firstName} ${this.lastName}`; }
115
115
  * ```
116
116
  */
117
- export function computed(target, key, desc) {
118
- // Define-time: wrap getter with caching
119
- const getter = desc.get;
117
+ export function computed(target, context) {
118
+ const key = String(context.name);
120
119
  const cacheKey = Symbol(`computed:${key}`);
121
120
  const dirtyKey = Symbol(`dirty:${key}`);
122
- desc.get = function () {
121
+ // Track dirty key for scheduleUpdate invalidation
122
+ context.addInitializer(function () {
123
+ if (!this.constructor.prototype[COMPUTED_DIRTY]) {
124
+ this.constructor.prototype[COMPUTED_DIRTY] = [];
125
+ }
126
+ if (!this.constructor.prototype[COMPUTED_DIRTY].includes(dirtyKey)) {
127
+ this.constructor.prototype[COMPUTED_DIRTY].push(dirtyKey);
128
+ }
129
+ });
130
+ return function () {
123
131
  if (this[dirtyKey] !== false) {
124
- this[cacheKey] = getter.call(this);
132
+ this[cacheKey] = target.call(this);
125
133
  this[dirtyKey] = false;
126
134
  }
127
135
  return this[cacheKey];
128
136
  };
129
- // Track the dirty key so scheduleUpdate can dirty all computed properties
130
- if (!target[COMPUTED_DIRTY])
131
- target[COMPUTED_DIRTY] = [];
132
- target[COMPUTED_DIRTY].push(dirtyKey);
137
+ }
138
+ // ── @store decorator ──
139
+ const STORE_META = Symbol("loom:store:meta");
140
+ /**
141
+ * Create a deep proxy that intercepts mutations and notifies the Reactive.
142
+ */
143
+ function createDeepProxy(obj, onChange, persist) {
144
+ const proxyCache = new WeakMap();
145
+ function wrap(target) {
146
+ if (target === null || typeof target !== "object")
147
+ return target;
148
+ if (proxyCache.has(target))
149
+ return proxyCache.get(target);
150
+ const proxy = new Proxy(target, {
151
+ get(t, p, receiver) {
152
+ const value = Reflect.get(t, p, receiver);
153
+ if (value !== null && typeof value === "object" && typeof p !== "symbol") {
154
+ return wrap(value);
155
+ }
156
+ return value;
157
+ },
158
+ set(t, p, value, receiver) {
159
+ const result = Reflect.set(t, p, value, receiver);
160
+ if (persist) {
161
+ persist.storage.set(persist.key, JSON.stringify(obj));
162
+ }
163
+ onChange();
164
+ return result;
165
+ },
166
+ deleteProperty(t, p) {
167
+ const result = Reflect.deleteProperty(t, p);
168
+ if (persist) {
169
+ persist.storage.set(persist.key, JSON.stringify(obj));
170
+ }
171
+ onChange();
172
+ return result;
173
+ },
174
+ });
175
+ proxyCache.set(target, proxy);
176
+ return proxy;
177
+ }
178
+ return wrap(obj);
179
+ }
180
+ /**
181
+ * Component-scoped reactive store with optional persistence (auto-accessor).
182
+ *
183
+ * ```ts
184
+ * @store<TodoState>({ items: [], filter: "all" })
185
+ * accessor state!: TodoState;
186
+ * ```
187
+ */
188
+ export function store(defaults, persist) {
189
+ return (_target, context) => {
190
+ const key = String(context.name);
191
+ const reactiveKey = Symbol(`store:${key}`);
192
+ const proxyKey = Symbol(`store:proxy:${key}`);
193
+ return {
194
+ get() {
195
+ if (!this[reactiveKey]) {
196
+ const initial = JSON.parse(JSON.stringify(defaults));
197
+ const r = new Reactive(initial, persist);
198
+ this[reactiveKey] = r;
199
+ r.subscribe(() => this.scheduleUpdate?.());
200
+ const notifyChange = () => this.scheduleUpdate?.();
201
+ this[proxyKey] = createDeepProxy(r.value, notifyChange, persist);
202
+ }
203
+ return this[proxyKey];
204
+ },
205
+ set(val) {
206
+ if (!this[reactiveKey]) {
207
+ const r = new Reactive(val, persist);
208
+ this[reactiveKey] = r;
209
+ r.subscribe(() => this.scheduleUpdate?.());
210
+ const notifyChange = () => this.scheduleUpdate?.();
211
+ this[proxyKey] = createDeepProxy(r.value, notifyChange, persist);
212
+ }
213
+ else {
214
+ this[reactiveKey].set(val);
215
+ const notifyChange = () => this.scheduleUpdate?.();
216
+ this[proxyKey] = createDeepProxy(this[reactiveKey].value, notifyChange, persist);
217
+ }
218
+ },
219
+ };
220
+ };
133
221
  }
134
222
  //# sourceMappingURL=decorators.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../src/store/decorators.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../src/store/decorators.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,6 @@ export { Reactive, CollectionStore } from "./reactive";
7
7
  export type { Subscriber, Updater, Identifiable } from "./reactive";
8
8
  export { MemoryStorage, LocalAdapter, SessionAdapter, LocalMedium, SessionMedium } from "./storage";
9
9
  export type { StorageAdapter, StorageMedium, PersistOptions } from "./storage";
10
- export { reactive, prop, computed, params, routeQuery } from "./decorators";
10
+ export { reactive, prop, computed, params, routeQuery, store } from "./decorators";
11
11
  export { watch } from "./watch";
12
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACvD,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACpG,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG/E,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG5E,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACvD,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGpE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACpG,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG/E,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAGnF,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC"}
@@ -8,7 +8,7 @@ export { Reactive, CollectionStore } from "./reactive";
8
8
  // Storage adapters
9
9
  export { MemoryStorage, LocalAdapter, SessionAdapter, LocalMedium, SessionMedium } from "./storage";
10
10
  // Decorators
11
- export { reactive, prop, computed, params, routeQuery } from "./decorators";
11
+ export { reactive, prop, computed, params, routeQuery, store } from "./decorators";
12
12
  // Watch
13
13
  export { watch } from "./watch";
14
14
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,sBAAsB;AACtB,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGvD,mBAAmB;AACnB,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAGpG,aAAa;AACb,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE5E,QAAQ;AACR,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,sBAAsB;AACtB,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGvD,mBAAmB;AACnB,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAGpG,aAAa;AACb,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAEnF,QAAQ;AACR,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC"}
@@ -1,27 +1,28 @@
1
1
  /**
2
- * Loom — Store @watch
2
+ * Loom — Store @watch (TC39 Stage 3)
3
3
  *
4
4
  * Form 1: Watch a local @reactive field by name
5
5
  * Form 2: Watch a direct Reactive/CollectionStore instance
6
6
  */
7
7
  /**
8
- * React to local @reactive field changes or direct store changes.
8
+ * Watch a local @reactive field or an external Reactive instance.
9
9
  *
10
- * Local @reactive field stores metadata consumed by @reactive's subscriber wiring:
10
+ * Form 1local field:
11
11
  * ```ts
12
- * @watch("value")
13
- * onValueChange(curr: number, prev: number) { ... }
12
+ * @watch("count")
13
+ * onCount(val: number, prev: number) { }
14
14
  * ```
15
15
  *
16
- * Direct Reactive/CollectionStore instance subscribes on connect, cleans up on disconnect:
16
+ * Form 2external Reactive:
17
17
  * ```ts
18
- * @watch(todos)
19
- * onTodosChange(items: Todo[], prev: Todo[]) { ... }
18
+ * const counter = new Reactive(0);
19
+ * @watch(counter)
20
+ * onCounter(val: number, prev: number) { … }
20
21
  * ```
21
22
  */
22
- export declare function watch(field: string): (target: any, key: string) => void;
23
+ export declare function watch(field: string): (method: Function, context: ClassMethodDecoratorContext) => void;
23
24
  export declare function watch(store: {
24
25
  subscribe: Function;
25
- value: any;
26
- }): (target: any, key: string) => void;
26
+ value: unknown;
27
+ }): (method: Function, context: ClassMethodDecoratorContext) => void;
27
28
  //# sourceMappingURL=watch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/store/watch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;AACzE,wBAAgB,KAAK,CAAC,KAAK,EAAE;IAAE,SAAS,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC"}
1
+ {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/store/watch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAC;AACvG,wBAAgB,KAAK,CAAC,KAAK,EAAE;IAAE,SAAS,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAC"}
@@ -1,29 +1,34 @@
1
1
  /**
2
- * Loom — Store @watch
2
+ * Loom — Store @watch (TC39 Stage 3)
3
3
  *
4
4
  * Form 1: Watch a local @reactive field by name
5
5
  * Form 2: Watch a direct Reactive/CollectionStore instance
6
6
  */
7
- import { WATCHERS } from "../decorators/symbols";
7
+ import { WATCHERS, CONNECT_HOOKS } from "../decorators/symbols";
8
8
  export function watch(target) {
9
- return (proto, key) => {
9
+ return (method, context) => {
10
+ const key = String(context.name);
10
11
  if (typeof target === "string") {
11
12
  // Form 1: local @reactive field — store metadata for @reactive to wire
12
- if (!proto[WATCHERS])
13
- proto[WATCHERS] = [];
14
- proto[WATCHERS].push({ field: target, key });
13
+ context.addInitializer(function () {
14
+ if (!this[WATCHERS])
15
+ this[WATCHERS] = [];
16
+ this[WATCHERS].push({ field: target, key });
17
+ });
15
18
  }
16
19
  else if (typeof target === "object" && typeof target.subscribe === "function") {
17
- // Form 2: direct Reactive instance — subscribe on connect
18
- const orig = proto.connectedCallback;
19
- proto.connectedCallback = function () {
20
- orig?.call(this);
21
- const unsub = target.subscribe((v, p) => {
22
- this[key](v, p);
23
- this.scheduleUpdate?.();
20
+ // Form 2: direct Reactive instance — subscribe on connect via CONNECT_HOOKS
21
+ context.addInitializer(function () {
22
+ if (!this[CONNECT_HOOKS])
23
+ this[CONNECT_HOOKS] = [];
24
+ this[CONNECT_HOOKS].push((el) => {
25
+ const unsub = target.subscribe((v, prev) => {
26
+ method.call(el, v, prev);
27
+ el.scheduleUpdate?.();
28
+ });
29
+ return unsub;
24
30
  });
25
- this.track(unsub);
26
- };
31
+ });
27
32
  }
28
33
  };
29
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/store/watch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAmBjD,MAAM,UAAU,KAAK,CAAC,MAAoD;IACxE,OAAO,CAAC,KAAU,EAAE,GAAW,EAAE,EAAE;QACjC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,uEAAuE;YACvE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;gBAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;YAC3C,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/C,CAAC;aAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;YAChF,0DAA0D;YAC1D,MAAM,IAAI,GAAG,KAAK,CAAC,iBAAiB,CAAC;YACrC,KAAK,CAAC,iBAAiB,GAAG;gBACxB,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;gBACjB,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAM,EAAE,CAAM,EAAE,EAAE;oBAChD,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;oBAChB,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC1B,CAAC,CAAC,CAAC;gBACH,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACpB,CAAC,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/store/watch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAoBhE,MAAM,UAAU,KAAK,CAAC,MAAwD;IAC5E,OAAO,CAAC,MAAgB,EAAE,OAAoC,EAAE,EAAE;QAChE,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAEjC,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,uEAAuE;YACvE,OAAO,CAAC,cAAc,CAAC;gBACrB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;oBAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;gBACzC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YAC9C,CAAC,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;YAChF,4EAA4E;YAC5E,OAAO,CAAC,cAAc,CAAC;gBACrB,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;oBAAE,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC;gBACnD,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,EAAO,EAAE,EAAE;oBACnC,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAU,EAAE,IAAa,EAAE,EAAE;wBAC3D,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;wBACzB,EAAE,CAAC,cAAc,EAAE,EAAE,CAAC;oBACxB,CAAC,CAAC,CAAC;oBACH,OAAO,KAAK,CAAC;gBACf,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Loom — Testing harness
3
+ *
4
+ * Utilities for mounting components in tests and waiting for renders.
5
+ *
6
+ * ```ts
7
+ * import { fixture, fixtureHTML, cleanup, nextRender } from "@toyz/loom/testing";
8
+ *
9
+ * // Mount by tag name
10
+ * const el = await fixture<MyCounter>("my-counter");
11
+ * el.count = 5;
12
+ * await el.updateComplete;
13
+ * expect(el.shadow.querySelector("span")?.textContent).toBe("5");
14
+ *
15
+ * // Mount from an HTML template string
16
+ * const el2 = await fixtureHTML<MyCard>(`<my-card title="Hello">content</my-card>`);
17
+ * expect(el2.shadow.querySelector("h2")?.textContent).toBe("Hello");
18
+ *
19
+ * cleanup(); // removes all fixtures
20
+ * ```
21
+ */
22
+ import type { LoomElement } from "./element/element";
23
+ /** Type for elements returned by fixture helpers */
24
+ type Rendered<T> = T & {
25
+ updateComplete: Promise<void>;
26
+ };
27
+ /**
28
+ * Mount a component by tag name and wait for its first render.
29
+ * Returns the element instance with full type access.
30
+ *
31
+ * @param tag Custom element tag name
32
+ * @param attrs Optional attribute key/value pairs to set before mount
33
+ */
34
+ export declare function fixture<T extends HTMLElement = LoomElement>(tag: string, attrs?: Record<string, string>): Promise<Rendered<T>>;
35
+ /**
36
+ * Mount a component from an HTML template string and wait for its first render.
37
+ * Useful for testing slot content, nested components, and attribute combinations.
38
+ *
39
+ * Returns the first child element with full type access.
40
+ *
41
+ * @param html HTML string containing the component to mount
42
+ */
43
+ export declare function fixtureHTML<T extends HTMLElement = LoomElement>(html: string): Promise<Rendered<T>>;
44
+ /**
45
+ * Remove all fixture containers from the document.
46
+ * Call this in afterEach or test teardown.
47
+ */
48
+ export declare function cleanup(): void;
49
+ /**
50
+ * Wait for a Loom element to complete its next render cycle.
51
+ * Useful after programmatic state changes.
52
+ */
53
+ export declare function nextRender(): Promise<void>;
54
+ export {};
55
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,oDAAoD;AACpD,KAAK,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG;IAAE,cAAc,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAEzD;;;;;;GAMG;AACH,wBAAsB,OAAO,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC/D,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAkBtB;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EACnE,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CActB;AAED;;;GAGG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAK9B;AAED;;;GAGG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAEhD"}