@avsbhq/node 1.7.2 → 1.9.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.
package/README.md CHANGED
@@ -35,8 +35,8 @@ if (!result.success) {
35
35
  // Bind the context once per request.
36
36
  const user = server.forUser({ kind: 'user', key: 'u_123', plan: 'pro' });
37
37
 
38
- const flag = user.getBoolFlag('new-checkout-flow', false);
39
- const checkoutTemplate = flag.isEnabled() ? 'checkout-v2' : 'checkout-v1';
38
+ const flag = user.getBoolFlag('new_checkout_flow', false);
39
+ const checkoutTemplate = flag.isEnabled() ? 'checkout_v2' : 'checkout_v1';
40
40
 
41
41
  user.track('checkout_started', { revenue: 99.0 });
42
42
  ```
@@ -139,7 +139,7 @@ const user = server.forUser({
139
139
  country: req.user.country,
140
140
  });
141
141
 
142
- const flag = user.getBoolFlag('checkout-v2', false);
142
+ const flag = user.getBoolFlag('checkout_v2', false);
143
143
  user.track('page_viewed');
144
144
  ```
145
145
 
@@ -150,7 +150,7 @@ user.track('page_viewed');
150
150
  Context is the THIRD POSITIONAL argument on every evaluation call, matching `@avsbhq/edge`:
151
151
 
152
152
  ```ts
153
- const flag = server.getBoolFlag('checkout-v2', false, {
153
+ const flag = server.getBoolFlag('checkout_v2', false, {
154
154
  kind: 'user',
155
155
  key: 'u_123',
156
156
  plan: 'pro',
@@ -175,7 +175,7 @@ const user = server.forUser({
175
175
  Audiences built with the commerce condition types (cart value, products viewed, purchase history) read fixed attribute names off the context:
176
176
 
177
177
  ```ts
178
- const flag = server.getBoolFlag('big-cart-banner', false, {
178
+ const flag = server.getBoolFlag('big_cart_banner', false, {
179
179
  kind: 'user',
180
180
  key: 'u_123',
181
181
  cartValue: 120.5, // decimal MAJOR units of the project currency
@@ -290,7 +290,7 @@ interface Flag<T = unknown> {
290
290
  ```ts
291
291
  import { DecideOption } from '@avsbhq/node';
292
292
 
293
- user.getBoolFlag('checkout-v2', false, {
293
+ user.getBoolFlag('checkout_v2', false, {
294
294
  decideOptions: [DecideOption.DISABLE_EXPOSURE],
295
295
  });
296
296
  ```
@@ -314,12 +314,12 @@ Called without a context, or before the datafile loads, it returns `{}` AND warn
314
314
  ### Overrides, for QA and testing
315
315
 
316
316
  ```ts
317
- server.setOverrideForUser('checkout-v2', 'u_qa', 'on');
318
- server.setOverrideForUser('checkout-v2', 'acme', 'on', 'organization'); // explicit kind
319
- server.setGlobalOverride('checkout-v2', 'on');
317
+ server.setOverrideForUser('checkout_v2', 'u_qa', 'on');
318
+ server.setOverrideForUser('checkout_v2', 'acme', 'on', 'organization'); // explicit kind
319
+ server.setGlobalOverride('checkout_v2', 'on');
320
320
 
321
- server.clearOverrideForUser('checkout-v2', 'u_qa');
322
- server.clearGlobalOverride('checkout-v2');
321
+ server.clearOverrideForUser('checkout_v2', 'u_qa');
322
+ server.clearGlobalOverride('checkout_v2');
323
323
  server.clearAllOverrides();
324
324
  ```
325
325
 
@@ -327,8 +327,8 @@ Per-request forced decisions, which never touch the shared state above:
327
327
 
328
328
  ```ts
329
329
  const user = server.forUser({ kind: 'user', key: 'u_qa' });
330
- user.setForcedDecision('checkout-v2', { variationKey: 'on' });
331
- user.clearForcedDecision('checkout-v2');
330
+ user.setForcedDecision('checkout_v2', { variationKey: 'on' });
331
+ user.clearForcedDecision('checkout_v2');
332
332
  ```
333
333
 
334
334
  ---
@@ -380,12 +380,12 @@ Purchases are queued before ready exactly like `track`. They used to be dropped
380
380
  ### Manual exposure
381
381
 
382
382
  ```ts
383
- const flag = user.getBoolFlag('checkout-v2', false, {
383
+ const flag = user.getBoolFlag('checkout_v2', false, {
384
384
  decideOptions: [DecideOption.DISABLE_EXPOSURE],
385
385
  });
386
386
 
387
387
  // Later, at the point the visitor actually sees the variation:
388
- user.manualExposure('checkout-v2');
388
+ user.manualExposure('checkout_v2');
389
389
  ```
390
390
 
391
391
  ---
@@ -474,7 +474,7 @@ actually being served.
474
474
  - **Where it shows up**: Environments in the project sidebar, in the dashboard. Each
475
475
  environment shows Connected, Stale, or Not connected, plus the `sdkType` and
476
476
  `sdkVersion` that last reported, which is how you see "production is running
477
- @avsbhq/node 1.6.2". An environment goes Stale after five minutes without a
477
+ @avsbhq/node 1.8.0". An environment goes Stale after five minutes without a
478
478
  heartbeat.
479
479
  - **Turning it off**: publish a datafile with no `heartbeatEndpoint`. The SDK
480
480
  never guesses a host, so an absent field simply means no heartbeats.
@@ -520,6 +520,17 @@ server.utils.hydrateBootstrap(blob); // blob must carry `datafile`
520
520
 
521
521
  `hydrateBootstrap` THROWS on a blob that carries only a pre-evaluated snapshot: a snapshot belongs to one context, and a server evaluates a different context per request, so adopting one process-wide would serve one user's values to another. Serialize with `{ includeDatafile: true }`, or pass `bootstrap: blob.datafile`.
522
522
 
523
+ ### Fresh snapshots
524
+
525
+ ```ts
526
+ const ctx = { kind: 'user', key: 'user-123' };
527
+
528
+ const snap = server.utils.snapshot(ctx); // values in memory, no network call
529
+ const fresh = await server.utils.snapshotFresh(ctx); // fetches the datafile first
530
+ ```
531
+
532
+ `snapshotFresh` and `refreshAndSnapshot` call `refreshDatafile()` before they read, so the snapshot reflects the flags published right now. A failed fetch keeps the datafile already in memory, logs a warning, and the snapshot shows that datafile. Before 1.9.0 both helpers skipped the fetch and returned the in-memory values.
533
+
523
534
  For Next.js SSR see `@avsbhq/next`.
524
535
 
525
536
  ---
@@ -587,7 +598,7 @@ import { createMockServer, flagsFromTestData, TestData } from '@avsbhq/test';
587
598
 
588
599
  const server = createMockServer(
589
600
  flagsFromTestData([
590
- TestData.flag('checkout-v2')
601
+ TestData.flag('checkout_v2')
591
602
  .booleanFlag()
592
603
  .variationForUser('u_paying', true)
593
604
  .fallthroughVariation(false)
@@ -596,7 +607,7 @@ const server = createMockServer(
596
607
  );
597
608
 
598
609
  const user = server.forUser({ kind: 'user', key: 'u_paying' });
599
- expect(user.getBoolFlag('checkout-v2', false).value).toBe(true);
610
+ expect(user.getBoolFlag('checkout_v2', false).value).toBe(true);
600
611
  expect(server._evaluations()).toHaveLength(1);
601
612
  ```
602
613
 
@@ -630,7 +641,7 @@ app.use(
630
641
 
631
642
  // Deep inside a service, with no `req` in scope:
632
643
  function pricingService() {
633
- return getRequestClient()?.getBoolFlag('new-pricing', false).value ?? false;
644
+ return getRequestClient()?.getBoolFlag('new_pricing', false).value ?? false;
634
645
  }
635
646
  ```
636
647
 
@@ -640,8 +651,8 @@ Every route then reads flags off `req.avsb`:
640
651
  // docs-example: not typechecked here, because express is not a dependency of the A vs B repository, so a route handler's Request and Response types cannot be resolved
641
652
 
642
653
  app.get('/checkout', (req, res) => {
643
- const flag = req.avsb.getBoolFlag('new-checkout-flow', false);
644
- res.render(flag.isEnabled() ? 'checkout-v2' : 'checkout-v1');
654
+ const flag = req.avsb.getBoolFlag('new_checkout_flow', false);
655
+ res.render(flag.isEnabled() ? 'checkout_v2' : 'checkout_v1');
645
656
  });
646
657
  ```
647
658
 
@@ -682,7 +693,7 @@ await app.register(fastifyPlugin, {
682
693
  });
683
694
 
684
695
  app.get('/features', (req, reply) => {
685
- reply.send({ enabled: req.avsb.getBoolFlag('new-feature', false).isEnabled() });
696
+ reply.send({ enabled: req.avsb.getBoolFlag('new_feature', false).isEnabled() });
686
697
  });
687
698
  ```
688
699
 
@@ -716,7 +727,7 @@ The Redis service keeps evaluation synchronous: a lookup is a cache hit or null,
716
727
 
717
728
  ```ts
718
729
  const user = server.forUser({ kind: 'user', key: 'u_123' });
719
- user.getBoolFlag('checkout-v2', false);
730
+ user.getBoolFlag('checkout_v2', false);
720
731
 
721
732
  const entries = user.getDecisionLog().entries;
722
733
  // [{ flagKey, value, variationKey, source, ruleId, ruleType, reasons,