@avsbhq/vue 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -23
- package/dist/{chunk-FUADAPSW.js → chunk-DHNLEJ7I.js} +9 -0
- package/dist/index.cjs +9 -0
- package/dist/index.js +1 -1
- package/dist/pinia.d.cts +1 -1
- package/dist/pinia.d.ts +1 -1
- package/dist/testing.cjs +9 -0
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -42,11 +42,11 @@ import type { ComputedRef } from 'vue';
|
|
|
42
42
|
import type { Flag } from '@avsbhq/vue';
|
|
43
43
|
|
|
44
44
|
// Keep the ref: reading `.value` here instead would freeze the first value.
|
|
45
|
-
const newCheckout: ComputedRef<Flag<boolean>> = useBoolFlag('
|
|
45
|
+
const newCheckout: ComputedRef<Flag<boolean>> = useBoolFlag('new_checkout_flow', false);
|
|
46
46
|
const track = useTrack();
|
|
47
47
|
|
|
48
48
|
// The visitor is about to see this decision, so record it once.
|
|
49
|
-
useExposure('
|
|
49
|
+
useExposure('new_checkout_flow');
|
|
50
50
|
</script>
|
|
51
51
|
|
|
52
52
|
<template>
|
|
@@ -62,7 +62,7 @@ Both props matter: without `context` the visitor gets a persisted anonymous id,
|
|
|
62
62
|
|
|
63
63
|
## 3. SDK keys
|
|
64
64
|
|
|
65
|
-
Get the SDK key for the environment you want from your project: **app.avsb.cloud**, then **
|
|
65
|
+
Get the SDK key for the environment you want from your project: **app.avsb.cloud**, then **Environments** in the project sidebar. The format is `sdk_<environment>_<id>`, for example `sdk_production_ttqm0eaj4vth1krcb2xn`.
|
|
66
66
|
|
|
67
67
|
There is one SDK key per environment, and "SDK key" is its only name. There is no separate client key and server key to choose between.
|
|
68
68
|
|
|
@@ -232,7 +232,7 @@ them and it is built from the union of this project's real flag keys, so a typo
|
|
|
232
232
|
is a compile error and your editor completes the list:
|
|
233
233
|
|
|
234
234
|
```bash
|
|
235
|
-
npx
|
|
235
|
+
npx @avsbhq/cli codegen --output src/generated/flags.ts
|
|
236
236
|
```
|
|
237
237
|
|
|
238
238
|
The generated file declares your flags twice on purpose: an importable
|
|
@@ -243,15 +243,15 @@ teaches every composable in this package which keys exist. Its important parts:
|
|
|
243
243
|
// AUTO-GENERATED by @avsbhq/cli codegen. Do not edit by hand.
|
|
244
244
|
|
|
245
245
|
export interface FlagValues {
|
|
246
|
-
'
|
|
247
|
-
'
|
|
246
|
+
'checkout_v2': boolean
|
|
247
|
+
'hero_copy': 'control' | 'variant_a'
|
|
248
248
|
'theme': { primary: string }
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
declare global {
|
|
252
252
|
interface AvsbFlags {
|
|
253
|
-
'
|
|
254
|
-
'
|
|
253
|
+
'checkout_v2': boolean
|
|
254
|
+
'hero_copy': 'control' | 'variant_a'
|
|
255
255
|
'theme': { primary: string }
|
|
256
256
|
}
|
|
257
257
|
}
|
|
@@ -261,14 +261,14 @@ declare global {
|
|
|
261
261
|
import { useBoolFlag, useJsonFlag } from '@avsbhq/vue';
|
|
262
262
|
import type { FlagValues } from './generated/flags';
|
|
263
263
|
|
|
264
|
-
const checkout = useBoolFlag('
|
|
264
|
+
const checkout = useBoolFlag('checkout_v2', false);
|
|
265
265
|
|
|
266
266
|
// A JSON flag types its payload from the generated table:
|
|
267
267
|
const theme = useJsonFlag<FlagValues['theme']>('theme', { primary: '#111' });
|
|
268
268
|
|
|
269
269
|
// Once the generated file exists, this line stops compiling:
|
|
270
|
-
// '
|
|
271
|
-
const typo = useBoolFlag('
|
|
270
|
+
// 'chekcout_v2' is not a flag key.
|
|
271
|
+
const typo = useBoolFlag('chekcout_v2', false);
|
|
272
272
|
```
|
|
273
273
|
|
|
274
274
|
One real consequence for reactive keys: a `Ref<string>` no longer satisfies
|
|
@@ -289,7 +289,7 @@ exported from `@avsbhq/core`).
|
|
|
289
289
|
`useFlag` returns a `ComputedRef<Flag<T>>`. Vue refs are unwrapped with `.value`, and the flag's own field is also called `value`, so in `<script setup>` you write:
|
|
290
290
|
|
|
291
291
|
```ts
|
|
292
|
-
const checkout = useFlag('
|
|
292
|
+
const checkout = useFlag('new_checkout_flow', false);
|
|
293
293
|
|
|
294
294
|
checkout.value.value; // boolean (ref unwrap, then Flag.value)
|
|
295
295
|
checkout.value.isEnabled(); // boolean
|
|
@@ -312,11 +312,11 @@ Two ways to make it read better:
|
|
|
312
312
|
import { computed } from 'vue';
|
|
313
313
|
|
|
314
314
|
// 1. Ask for the value instead of the Flag
|
|
315
|
-
const showNewCheckout = useFlagValue('
|
|
315
|
+
const showNewCheckout = useFlagValue('new_checkout_flow', false);
|
|
316
316
|
showNewCheckout.value; // boolean, one .value everywhere
|
|
317
317
|
|
|
318
318
|
// 2. Destructure with toRefs-style naming at the top of setup
|
|
319
|
-
const checkoutFlag = useFlag('
|
|
319
|
+
const checkoutFlag = useFlag('new_checkout_flow', false);
|
|
320
320
|
const isNewCheckout = computed(() => checkoutFlag.value.isEnabled());
|
|
321
321
|
```
|
|
322
322
|
|
|
@@ -376,13 +376,13 @@ A rule can bucket on `user.key` while matching an audience condition on `organiz
|
|
|
376
376
|
import { useFlag, useBoolFlag, useJsonFlag } from '@avsbhq/vue';
|
|
377
377
|
import type { Flag } from '@avsbhq/vue';
|
|
378
378
|
|
|
379
|
-
const darkMode = useBoolFlag('
|
|
379
|
+
const darkMode = useBoolFlag('dark_mode', false);
|
|
380
380
|
|
|
381
381
|
interface ApiConfig {
|
|
382
382
|
timeout: number;
|
|
383
383
|
retries: number;
|
|
384
384
|
}
|
|
385
|
-
const config = useJsonFlag<ApiConfig>('
|
|
385
|
+
const config = useJsonFlag<ApiConfig>('api_config', { timeout: 5000, retries: 3 });
|
|
386
386
|
const timeout: number = config.value.value.timeout;
|
|
387
387
|
```
|
|
388
388
|
|
|
@@ -395,7 +395,7 @@ A default value is always required, and it is what you get before the SDK is rea
|
|
|
395
395
|
```ts
|
|
396
396
|
import { ref } from 'vue';
|
|
397
397
|
|
|
398
|
-
const selected = ref('
|
|
398
|
+
const selected = ref('feature_a');
|
|
399
399
|
const flag = useFlag(selected, false); // re-subscribes when `selected` changes
|
|
400
400
|
```
|
|
401
401
|
|
|
@@ -417,7 +417,7 @@ Reads during render never record an exposure. That is deliberate: an exposure me
|
|
|
417
417
|
import { useExposure } from '@avsbhq/vue';
|
|
418
418
|
|
|
419
419
|
// In the component that actually shows the variation:
|
|
420
|
-
useExposure('
|
|
420
|
+
useExposure('new_checkout_flow');
|
|
421
421
|
```
|
|
422
422
|
|
|
423
423
|
`useExposure` fires on mount, once, or as soon as the SDK becomes ready if it is not yet. It is also the piece that makes server-rendered variants visible in results: if a value arrived as a prop from the server, call `useExposure` where it is rendered.
|
|
@@ -427,7 +427,7 @@ Only decisions that belong in an experiment (A/B rules, holdouts, bandits) produ
|
|
|
427
427
|
For side effects that are not rendering:
|
|
428
428
|
|
|
429
429
|
```ts
|
|
430
|
-
useFlagSubscription('
|
|
430
|
+
useFlagSubscription('new_checkout_flow', (flag) => {
|
|
431
431
|
analytics.track('checkout_variant_assigned', { variationKey: flag.variationKey });
|
|
432
432
|
});
|
|
433
433
|
```
|
|
@@ -565,7 +565,7 @@ import { AvsbTestProvider } from '@avsbhq/vue/testing';
|
|
|
565
565
|
import CheckoutButton from './CheckoutButton.vue';
|
|
566
566
|
|
|
567
567
|
const wrapper = mount(AvsbTestProvider, {
|
|
568
|
-
props: { flags: { '
|
|
568
|
+
props: { flags: { 'checkout_v2': true } },
|
|
569
569
|
slots: { default: CheckoutButton },
|
|
570
570
|
});
|
|
571
571
|
```
|
|
@@ -580,15 +580,15 @@ Drive the client directly when that is simpler:
|
|
|
580
580
|
import { createTestClient, createTestDatafile } from '@avsbhq/vue/testing';
|
|
581
581
|
|
|
582
582
|
const client = createTestClient({
|
|
583
|
-
flags: { '
|
|
583
|
+
flags: { 'checkout_v2': true },
|
|
584
584
|
context: { kind: 'user', key: 'u_1' },
|
|
585
585
|
});
|
|
586
586
|
|
|
587
|
-
expect(client.getBoolFlag('
|
|
587
|
+
expect(client.getBoolFlag('checkout_v2', false).isEnabled()).toBe(true);
|
|
588
588
|
|
|
589
589
|
// Publish a new datafile and watch subscribers wake up.
|
|
590
590
|
client.applyDatafileBootstrap(
|
|
591
|
-
createTestDatafile({ '
|
|
591
|
+
createTestDatafile({ 'checkout_v2': false }, { publishedAt: '2024-06-01T00:00:00.000Z' }),
|
|
592
592
|
);
|
|
593
593
|
```
|
|
594
594
|
|
|
@@ -57,6 +57,12 @@ function createProviderState(client) {
|
|
|
57
57
|
|
|
58
58
|
// src/internal/resolveClient.ts
|
|
59
59
|
import { AvsbClient } from "@avsbhq/browser";
|
|
60
|
+
|
|
61
|
+
// src/version.ts
|
|
62
|
+
var SDK_VERSION = "1.1.2";
|
|
63
|
+
var SDK_TYPE = "@avsbhq/vue";
|
|
64
|
+
|
|
65
|
+
// src/internal/resolveClient.ts
|
|
60
66
|
var MISUSE_MESSAGE = `[avsb] <AvsbProvider> needs either an sdk-key (Mode A) or a client (Mode B). Mode A: <AvsbProvider sdk-key="sdk_production_..." :context="{ kind: 'user', key: userId }">. Mode B: <AvsbProvider :client="myClient">. The key for each environment is on app.avsb.cloud under Environments in the project sidebar.`;
|
|
61
67
|
function resolveClient(props) {
|
|
62
68
|
const injected = props.client;
|
|
@@ -69,6 +75,9 @@ function resolveClient(props) {
|
|
|
69
75
|
}
|
|
70
76
|
const clientOptions = {
|
|
71
77
|
...options ?? {},
|
|
78
|
+
// Report @avsbhq/vue on the heartbeat, unless a package wrapping this one
|
|
79
|
+
// (@avsbhq/nuxt) already named itself (N1-14).
|
|
80
|
+
wrapper: options?.wrapper ?? { sdkType: SDK_TYPE, sdkVersion: SDK_VERSION },
|
|
72
81
|
sdkKey,
|
|
73
82
|
...context !== void 0 ? { context } : {},
|
|
74
83
|
...bootstrap !== void 0 ? { bootstrap } : {}
|
package/dist/index.cjs
CHANGED
|
@@ -113,6 +113,12 @@ function createProviderState(client) {
|
|
|
113
113
|
|
|
114
114
|
// src/internal/resolveClient.ts
|
|
115
115
|
var import_browser = require("@avsbhq/browser");
|
|
116
|
+
|
|
117
|
+
// src/version.ts
|
|
118
|
+
var SDK_VERSION = "1.1.2";
|
|
119
|
+
var SDK_TYPE = "@avsbhq/vue";
|
|
120
|
+
|
|
121
|
+
// src/internal/resolveClient.ts
|
|
116
122
|
var MISUSE_MESSAGE = `[avsb] <AvsbProvider> needs either an sdk-key (Mode A) or a client (Mode B). Mode A: <AvsbProvider sdk-key="sdk_production_..." :context="{ kind: 'user', key: userId }">. Mode B: <AvsbProvider :client="myClient">. The key for each environment is on app.avsb.cloud under Environments in the project sidebar.`;
|
|
117
123
|
function resolveClient(props) {
|
|
118
124
|
const injected = props.client;
|
|
@@ -125,6 +131,9 @@ function resolveClient(props) {
|
|
|
125
131
|
}
|
|
126
132
|
const clientOptions = {
|
|
127
133
|
...options ?? {},
|
|
134
|
+
// Report @avsbhq/vue on the heartbeat, unless a package wrapping this one
|
|
135
|
+
// (@avsbhq/nuxt) already named itself (N1-14).
|
|
136
|
+
wrapper: options?.wrapper ?? { sdkType: SDK_TYPE, sdkVersion: SDK_VERSION },
|
|
128
137
|
sdkKey,
|
|
129
138
|
...context !== void 0 ? { context } : {},
|
|
130
139
|
...bootstrap !== void 0 ? { bootstrap } : {}
|
package/dist/index.js
CHANGED
package/dist/pinia.d.cts
CHANGED
|
@@ -18,6 +18,6 @@ interface AvsbStoreState {
|
|
|
18
18
|
reset: () => void;
|
|
19
19
|
track: (eventKey: string, payload?: TrackPayload) => void;
|
|
20
20
|
}
|
|
21
|
-
declare const useAvsbStore: pinia.StoreDefinition<"avsb", Pick<AvsbStoreState, never>, Pick<AvsbStoreState, "
|
|
21
|
+
declare const useAvsbStore: pinia.StoreDefinition<"avsb", Pick<AvsbStoreState, never>, Pick<AvsbStoreState, "error" | "client" | "status" | "degraded" | "flags">, Pick<AvsbStoreState, "identify" | "alias" | "reset" | "track">>;
|
|
22
22
|
|
|
23
23
|
export { type AvsbStoreState, useAvsbStore };
|
package/dist/pinia.d.ts
CHANGED
|
@@ -18,6 +18,6 @@ interface AvsbStoreState {
|
|
|
18
18
|
reset: () => void;
|
|
19
19
|
track: (eventKey: string, payload?: TrackPayload) => void;
|
|
20
20
|
}
|
|
21
|
-
declare const useAvsbStore: pinia.StoreDefinition<"avsb", Pick<AvsbStoreState, never>, Pick<AvsbStoreState, "
|
|
21
|
+
declare const useAvsbStore: pinia.StoreDefinition<"avsb", Pick<AvsbStoreState, never>, Pick<AvsbStoreState, "error" | "client" | "status" | "degraded" | "flags">, Pick<AvsbStoreState, "identify" | "alias" | "reset" | "track">>;
|
|
22
22
|
|
|
23
23
|
export { type AvsbStoreState, useAvsbStore };
|
package/dist/testing.cjs
CHANGED
|
@@ -170,6 +170,12 @@ function createProviderState(client) {
|
|
|
170
170
|
|
|
171
171
|
// src/internal/resolveClient.ts
|
|
172
172
|
var import_browser2 = require("@avsbhq/browser");
|
|
173
|
+
|
|
174
|
+
// src/version.ts
|
|
175
|
+
var SDK_VERSION = "1.1.2";
|
|
176
|
+
var SDK_TYPE = "@avsbhq/vue";
|
|
177
|
+
|
|
178
|
+
// src/internal/resolveClient.ts
|
|
173
179
|
var MISUSE_MESSAGE = `[avsb] <AvsbProvider> needs either an sdk-key (Mode A) or a client (Mode B). Mode A: <AvsbProvider sdk-key="sdk_production_..." :context="{ kind: 'user', key: userId }">. Mode B: <AvsbProvider :client="myClient">. The key for each environment is on app.avsb.cloud under Environments in the project sidebar.`;
|
|
174
180
|
function resolveClient(props) {
|
|
175
181
|
const injected = props.client;
|
|
@@ -182,6 +188,9 @@ function resolveClient(props) {
|
|
|
182
188
|
}
|
|
183
189
|
const clientOptions = {
|
|
184
190
|
...options ?? {},
|
|
191
|
+
// Report @avsbhq/vue on the heartbeat, unless a package wrapping this one
|
|
192
|
+
// (@avsbhq/nuxt) already named itself (N1-14).
|
|
193
|
+
wrapper: options?.wrapper ?? { sdkType: SDK_TYPE, sdkVersion: SDK_VERSION },
|
|
185
194
|
sdkKey,
|
|
186
195
|
...context !== void 0 ? { context } : {},
|
|
187
196
|
...bootstrap !== void 0 ? { bootstrap } : {}
|
package/dist/testing.d.cts
CHANGED
|
@@ -42,9 +42,9 @@ declare const AvsbTestProvider: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
42
42
|
default: undefined;
|
|
43
43
|
};
|
|
44
44
|
}>> & Readonly<{}>, {
|
|
45
|
+
client: VueAvsbClient;
|
|
45
46
|
flags: TestFlags;
|
|
46
47
|
context: EvalContext;
|
|
47
|
-
client: VueAvsbClient;
|
|
48
48
|
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
49
49
|
|
|
50
50
|
export { AvsbTestProvider };
|
package/dist/testing.d.ts
CHANGED
|
@@ -42,9 +42,9 @@ declare const AvsbTestProvider: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
42
42
|
default: undefined;
|
|
43
43
|
};
|
|
44
44
|
}>> & Readonly<{}>, {
|
|
45
|
+
client: VueAvsbClient;
|
|
45
46
|
flags: TestFlags;
|
|
46
47
|
context: EvalContext;
|
|
47
|
-
client: VueAvsbClient;
|
|
48
48
|
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
49
49
|
|
|
50
50
|
export { AvsbTestProvider };
|
package/dist/testing.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@avsbhq/vue",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Vue SDK for A vs B feature flags and experiments: composables, a provider component, an optional Pinia store, and test helpers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"avsb",
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"url": "https://github.com/avsbhq/a-vs-b/issues"
|
|
68
68
|
},
|
|
69
69
|
"scripts": {
|
|
70
|
+
"prebuild": "node ../avsb-core/scripts/writeSdkVersion.mjs",
|
|
70
71
|
"build": "tsup",
|
|
71
72
|
"dev": "tsup --watch",
|
|
72
73
|
"test": "vitest run",
|
|
@@ -82,11 +83,11 @@
|
|
|
82
83
|
}
|
|
83
84
|
},
|
|
84
85
|
"dependencies": {
|
|
85
|
-
"@avsbhq/browser": "^1.
|
|
86
|
-
"@avsbhq/core": "^1.
|
|
86
|
+
"@avsbhq/browser": "^1.5.0",
|
|
87
|
+
"@avsbhq/core": "^1.6.0"
|
|
87
88
|
},
|
|
88
89
|
"devDependencies": {
|
|
89
|
-
"@avsbhq/test": "^1.0
|
|
90
|
+
"@avsbhq/test": "^1.2.0",
|
|
90
91
|
"@vitejs/plugin-vue": "^5.0.0",
|
|
91
92
|
"@vue/test-utils": "^2.4.0",
|
|
92
93
|
"happy-dom": "^14.0.0",
|