@avsbhq/vue 1.1.0 → 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 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('new-checkout-flow', false);
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('new-checkout-flow');
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 **Settings**, then **Environments**. The format is `sdk_<environment>_<id>`, for example `sdk_production_ttqm0eaj4vth1krcb2xn`.
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 avsb codegen --output src/generated/flags.ts
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
- 'checkout-v2': boolean
247
- 'hero-copy': 'control' | 'variant-a'
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
- 'checkout-v2': boolean
254
- 'hero-copy': 'control' | 'variant-a'
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('checkout-v2', false);
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
- // 'chekcout-v2' is not a flag key.
271
- const typo = useBoolFlag('chekcout-v2', false);
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('new-checkout-flow', false);
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('new-checkout-flow', false);
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('new-checkout-flow', false);
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('dark-mode', false);
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>('api-config', { timeout: 5000, retries: 3 });
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('feature-a');
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('new-checkout-flow');
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('new-checkout-flow', (flag) => {
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: { 'checkout-v2': true } },
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: { 'checkout-v2': true },
583
+ flags: { 'checkout_v2': true },
584
584
  context: { kind: 'user', key: 'u_1' },
585
585
  });
586
586
 
587
- expect(client.getBoolFlag('checkout-v2', false).isEnabled()).toBe(true);
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({ 'checkout-v2': false }, { publishedAt: '2024-06-01T00:00:00.000Z' }),
591
+ createTestDatafile({ 'checkout_v2': false }, { publishedAt: '2024-06-01T00:00:00.000Z' }),
592
592
  );
593
593
  ```
594
594
 
@@ -10,7 +10,7 @@ import { consoleLogger } from "@avsbhq/core";
10
10
 
11
11
  // src/internal/providerState.ts
12
12
  import { ref, shallowRef } from "vue";
13
- var UNEXPLAINED_FAILURE = "[avsb] the SDK could not load a datafile and nothing was cached or bootstrapped, so every flag is returning the default you passed. Check the SDK key on app.avsb.cloud under Settings, then Environments.";
13
+ var UNEXPLAINED_FAILURE = "[avsb] the SDK could not load a datafile and nothing was cached or bootstrapped, so every flag is returning the default you passed. Check the SDK key on app.avsb.cloud under Environments in the project sidebar.";
14
14
  function createProviderState(client) {
15
15
  const status = ref(client.isReady() ? "ready" : "loading");
16
16
  const error = shallowRef(void 0);
@@ -57,7 +57,13 @@ function createProviderState(client) {
57
57
 
58
58
  // src/internal/resolveClient.ts
59
59
  import { AvsbClient } from "@avsbhq/browser";
60
- 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 Settings, then Environments.`;
60
+
61
+ // src/version.ts
62
+ var SDK_VERSION = "1.1.2";
63
+ var SDK_TYPE = "@avsbhq/vue";
64
+
65
+ // src/internal/resolveClient.ts
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;
63
69
  if (injected !== void 0 && injected !== null) {
@@ -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
@@ -66,7 +66,7 @@ function hasAvsbContext() {
66
66
 
67
67
  // src/internal/providerState.ts
68
68
  var import_vue2 = require("vue");
69
- var UNEXPLAINED_FAILURE = "[avsb] the SDK could not load a datafile and nothing was cached or bootstrapped, so every flag is returning the default you passed. Check the SDK key on app.avsb.cloud under Settings, then Environments.";
69
+ var UNEXPLAINED_FAILURE = "[avsb] the SDK could not load a datafile and nothing was cached or bootstrapped, so every flag is returning the default you passed. Check the SDK key on app.avsb.cloud under Environments in the project sidebar.";
70
70
  function createProviderState(client) {
71
71
  const status = (0, import_vue2.ref)(client.isReady() ? "ready" : "loading");
72
72
  const error = (0, import_vue2.shallowRef)(void 0);
@@ -113,7 +113,13 @@ function createProviderState(client) {
113
113
 
114
114
  // src/internal/resolveClient.ts
115
115
  var import_browser = require("@avsbhq/browser");
116
- 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 Settings, then Environments.`;
116
+
117
+ // src/version.ts
118
+ var SDK_VERSION = "1.1.2";
119
+ var SDK_TYPE = "@avsbhq/vue";
120
+
121
+ // src/internal/resolveClient.ts
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;
119
125
  if (injected !== void 0 && injected !== null) {
@@ -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
@@ -2,7 +2,7 @@ import {
2
2
  AvsbProvider,
3
3
  createProviderState,
4
4
  resolveClient
5
- } from "./chunk-7A3YSHQG.js";
5
+ } from "./chunk-DHNLEJ7I.js";
6
6
  import {
7
7
  AvsbInjectionKey,
8
8
  useAvsbContext
package/dist/testing.cjs CHANGED
@@ -123,7 +123,7 @@ function hasAvsbContext() {
123
123
 
124
124
  // src/internal/providerState.ts
125
125
  var import_vue2 = require("vue");
126
- var UNEXPLAINED_FAILURE = "[avsb] the SDK could not load a datafile and nothing was cached or bootstrapped, so every flag is returning the default you passed. Check the SDK key on app.avsb.cloud under Settings, then Environments.";
126
+ var UNEXPLAINED_FAILURE = "[avsb] the SDK could not load a datafile and nothing was cached or bootstrapped, so every flag is returning the default you passed. Check the SDK key on app.avsb.cloud under Environments in the project sidebar.";
127
127
  function createProviderState(client) {
128
128
  const status = (0, import_vue2.ref)(client.isReady() ? "ready" : "loading");
129
129
  const error = (0, import_vue2.shallowRef)(void 0);
@@ -170,7 +170,13 @@ function createProviderState(client) {
170
170
 
171
171
  // src/internal/resolveClient.ts
172
172
  var import_browser2 = require("@avsbhq/browser");
173
- 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 Settings, then Environments.`;
173
+
174
+ // src/version.ts
175
+ var SDK_VERSION = "1.1.2";
176
+ var SDK_TYPE = "@avsbhq/vue";
177
+
178
+ // src/internal/resolveClient.ts
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;
176
182
  if (injected !== void 0 && injected !== null) {
@@ -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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AvsbProvider
3
- } from "./chunk-7A3YSHQG.js";
3
+ } from "./chunk-DHNLEJ7I.js";
4
4
  import "./chunk-4XBADQFO.js";
5
5
 
6
6
  // src/testing.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@avsbhq/vue",
3
- "version": "1.1.0",
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.3.1",
86
- "@avsbhq/core": "^1.4.0"
86
+ "@avsbhq/browser": "^1.5.0",
87
+ "@avsbhq/core": "^1.6.0"
87
88
  },
88
89
  "devDependencies": {
89
- "@avsbhq/test": "^1.0.1",
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",