@toyz/loom-flags 0.1.0 → 0.1.1

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 (2) hide show
  1. package/README.md +209 -0
  2. package/package.json +2 -2
package/README.md ADDED
@@ -0,0 +1,209 @@
1
+ # @toyz/loom-flags
2
+
3
+ Decorator-driven feature flags for [Loom](https://github.com/Toyz/loom). Reactive, transport-swappable, real-time.
4
+
5
+ ```
6
+ npm install @toyz/loom-flags
7
+ ```
8
+
9
+ **One dependency:** `@toyz/loom`. That's it.
10
+
11
+ ---
12
+
13
+ ## Quick Start
14
+
15
+ ### 1. Create a Provider
16
+
17
+ ```ts
18
+ import { app } from "@toyz/loom";
19
+ import { FlagProvider } from "@toyz/loom-flags";
20
+
21
+ class MyFlagProvider extends FlagProvider {
22
+ isEnabled(flag: string, context?: Record<string, any>): boolean {
23
+ return this.flags.get(flag) ?? false;
24
+ }
25
+
26
+ getVariant<T = string>(flag: string, fallback: T): T {
27
+ const val = this.variants.get(flag);
28
+ return (val !== undefined ? val : fallback) as T;
29
+ }
30
+ }
31
+
32
+ const provider = new MyFlagProvider();
33
+ provider.set("dark-mode", true);
34
+ provider.set("beta-export", false);
35
+
36
+ app.use(FlagProvider, provider);
37
+ ```
38
+
39
+ ### 2. Use `@flag` on a Class
40
+
41
+ ```ts
42
+ import { flag } from "@toyz/loom-flags";
43
+
44
+ @component("new-dashboard")
45
+ @flag("new-dashboard")
46
+ class NewDashboard extends LoomElement {
47
+ update() {
48
+ if (!this.flagEnabled) return <div>Feature not available</div>;
49
+ return <div>Welcome to the new dashboard!</div>;
50
+ }
51
+ }
52
+ ```
53
+
54
+ The `@flag` class decorator injects a reactive `flagEnabled` property. When the flag changes at runtime, `scheduleUpdate()` is called automatically.
55
+
56
+ ### 3. Use `@flag` on a Method
57
+
58
+ ```ts
59
+ @component("data-tools")
60
+ class DataTools extends LoomElement {
61
+ @flag("beta-export")
62
+ handleExport() {
63
+ // Only runs when "beta-export" is enabled — no-op otherwise
64
+ downloadCSV(this.data);
65
+ }
66
+ }
67
+ ```
68
+
69
+ ### 4. Dynamic Context
70
+
71
+ Pass user info to the provider for targeted flag evaluation:
72
+
73
+ ```ts
74
+ @flag("premium-widgets", el => ({
75
+ userId: el.user.id,
76
+ plan: el.user.plan,
77
+ }))
78
+ class PremiumWidget extends LoomElement { ... }
79
+ ```
80
+
81
+ ### 5. Declarative with `<loom-flag>`
82
+
83
+ ```tsx
84
+ import "@toyz/loom-flags"; // registers <loom-flag>
85
+
86
+ <loom-flag name="beta-feature">
87
+ <new-widget slot="enabled" />
88
+ <span slot="disabled">Coming soon…</span>
89
+ </loom-flag>
90
+ ```
91
+
92
+ Swaps slots reactively when the flag changes — no component code required.
93
+
94
+ ---
95
+
96
+ ## Real-Time Updates
97
+
98
+ Providers can push flag changes at runtime. Every `@flag` and `<loom-flag>` re-evaluates instantly:
99
+
100
+ ```ts
101
+ // From a WebSocket handler, SSE listener, or polling loop:
102
+ provider.set("dark-mode", false); // toggles all @flag("dark-mode")
103
+ provider.setVariant("checkout", "b"); // updates variant value
104
+ ```
105
+
106
+ Under the hood, `set()` fires a `FlagChanged` event on the Loom bus. All subscribers react.
107
+
108
+ ---
109
+
110
+ ## API
111
+
112
+ ### `@flag(name, context?)`
113
+
114
+ Multi-kind decorator. Works on classes and methods.
115
+
116
+ | Target | Behavior |
117
+ |---|---|
118
+ | Class | Injects reactive `flagEnabled` + `flagName` properties |
119
+ | Method | Guards execution — no-op when flag is off |
120
+
121
+ ### `FlagProvider`
122
+
123
+ Abstract class — extend and register via DI.
124
+
125
+ ```ts
126
+ abstract class FlagProvider {
127
+ abstract isEnabled(flag: string, context?: Record<string, any>): boolean;
128
+ abstract getVariant<T = string>(flag: string, fallback: T): T;
129
+ set(flag: string, enabled: boolean): void;
130
+ setVariant(flag: string, value: string): void;
131
+ }
132
+ ```
133
+
134
+ ### `<loom-flag name="...">`
135
+
136
+ Built-in component for declarative flag gating.
137
+
138
+ | Slot | Shown when |
139
+ |---|---|
140
+ | `enabled` | Flag is on |
141
+ | `disabled` | Flag is off |
142
+
143
+ ### `FlagChanged`
144
+
145
+ Bus event dispatched when a flag changes.
146
+
147
+ ```ts
148
+ class FlagChanged extends LoomEvent {
149
+ readonly flag: string;
150
+ readonly enabled: boolean;
151
+ readonly variant?: string;
152
+ }
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Custom Providers
158
+
159
+ Integrate with any flag service:
160
+
161
+ ```ts
162
+ // LaunchDarkly
163
+ class LDProvider extends FlagProvider {
164
+ constructor(private client: LDClient) { super(); }
165
+
166
+ isEnabled(flag: string, context?: Record<string, any>): boolean {
167
+ return this.client.variation(flag, context, false);
168
+ }
169
+
170
+ getVariant<T = string>(flag: string, fallback: T): T {
171
+ return this.client.variation(flag, {}, fallback);
172
+ }
173
+ }
174
+
175
+ app.use(FlagProvider, new LDProvider(ldClient));
176
+ ```
177
+
178
+ One DI swap. Every `@flag` and `<loom-flag>` in the app uses the new provider. No component changes.
179
+
180
+ ---
181
+
182
+ ## Testing
183
+
184
+ ```ts
185
+ import { MockFlags } from "@toyz/loom-flags/testing";
186
+
187
+ const flags = new MockFlags();
188
+ app.use(FlagProvider, flags);
189
+
190
+ // Toggle flags
191
+ flags.enable("dark-mode");
192
+ flags.disable("beta-export");
193
+ flags.setVariant("checkout-flow", "variant-b");
194
+
195
+ // Assertions
196
+ flags.assertChecked("dark-mode");
197
+ flags.assertEnabled("dark-mode");
198
+ flags.assertDisabled("beta-export");
199
+ flags.assertNotChecked("unknown-flag");
200
+
201
+ // Reset between tests
202
+ flags.reset();
203
+ ```
204
+
205
+ ---
206
+
207
+ ## License
208
+
209
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toyz/loom-flags",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "Feature flags for Loom — decorator-driven with real-time reactive updates",
6
6
  "license": "MIT",
@@ -49,4 +49,4 @@
49
49
  "reactive",
50
50
  "web-components"
51
51
  ]
52
- }
52
+ }