Skip to content

Add Vue integration package#181

Open
Iineman2 wants to merge 4 commits into
tryabby:mainfrom
Iineman2:iineman2-clean-vue-integration
Open

Add Vue integration package#181
Iineman2 wants to merge 4 commits into
tryabby:mainfrom
Iineman2:iineman2-clean-vue-integration

Conversation

@Iineman2
Copy link
Copy Markdown

@Iineman2 Iineman2 commented May 11, 2026

Summary

  • add a clean @tryabby/vue package under packages/vue
  • implement typed Vue composables for A/B tests, feature flags, remote config, provider hydration/subscription, and user updates
  • add package README, changeset, workspace lockfile updates, and focused runtime/type tests

Demo video

Verification

  • pnpm install --filter @tryabby/vue... --prefer-offline
  • pnpm --filter @tryabby/core build
  • pnpm --filter @tryabby/vue exec vitest run
  • pnpm --filter @tryabby/vue build
  • pnpm exec biome check packages/vue/src packages/vue/tests packages/vue/vitest.config.ts packages/vue/README.md
  • git diff --check

/claim #68

Summary by CodeRabbit

  • New Features

    • Added a Vue 3 integration package (tryabby/vue) with typed composables for A/B tests, feature flags, and remote config, plus cookie-backed persistent storage and programmatic accessors.
  • Documentation

    • Added README with setup, provider usage, and composable examples.
  • Tests

    • Added tests validating runtime behavior and TypeScript typings for the Vue composables.

Review Change Stack

@vercel
Copy link
Copy Markdown

vercel Bot commented May 11, 2026

@tankylz is attempting to deploy a commit to the cstrnt's projects Team on Vercel.

A member of the Team first needs to authorize it.

@algora-pbc algora-pbc Bot mentioned this pull request May 11, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

Walkthrough

This PR adds a new @tryabby/vue package with a package manifest, build/test tooling, cookie-backed storage adapters, a createAbby factory, AbbyProvider, reactive composables for AB tests/flags/remote config, non-reactive getters/utilities, tests, README, and a changeset marking a minor version bump.

Changes

Vue Integration Package

Layer / File(s) Summary
Package Configuration
packages/vue/package.json
Package manifest defines distribution entrypoints (main, module, types), exports map for CJS/ESM/types, build scripts (tsup), test script (vitest), dependencies (@tryabby/core, js-cookie), and peer dependency vue.
Build & Test Tooling
packages/vue/tsconfig.json, packages/vue/vitest.config.ts
TypeScript config extends base with ESNext+DOM and vitest/globals; Vitest config enables Vue plugin, jsdom test environment, and globals.
Cookie-Backed Storage Services
packages/vue/src/StorageService.ts
CookieStorageService implementation and three singleton IStorageService adapters (TestStorageService, FlagStorageService, RemoteConfigStorageService) using key builders and 365-day default expiration.
Core Imports
packages/vue/src/index.ts (lines 1–30)
Vue composition and storage imports used to wire Abby into Vue composables and provider.
Type Definitions
packages/vue/src/index.ts (lines 31–50)
Generic InferValidator and ABTestReturnValue types to map validators to value types and resolve AB test return types with optional lookup mappings.
createAbby Factory
packages/vue/src/index.ts (lines 51–112)
Factory that instantiates Abby with browser-safe storage adapters for tests, flags, and remote config and prepares the package API surface.
Provider Component & Injection
packages/vue/src/index.ts (lines 114–156)
AbbyProvider initializes/shallow-refs project data, loads initial data when absent, subscribes to Abby updates, and provides data via Vue provide/inject.
useAbby Composable
packages/vue/src/index.ts (lines 158–216)
Reactive hook computing resolved variant (optional lookup mapping), sends PING on mount, and exposes onAct for submitting ACT events.
Reactive Flag & Config Hooks
packages/vue/src/index.ts (lines 217–259)
useFeatureFlag/useFeatureFlags and useRemoteConfig/useRemoteConfigVariables expose typed reactive flag and remote config values.
Non-Reactive Getters & Utilities
packages/vue/src/index.ts (lines 260–305)
Static getters getFeatureFlagValue, getRemoteConfig, getABTestValue, getVariants, getABResetFunction, and updateUserProperties.
Public API Export
packages/vue/src/index.ts (lines 307–323)
Exports the public API object with composables, AbbyProvider, getters/utilities, and __abby__ instance.
Test Suite
packages/vue/tests/createAbby.test.ts
Runtime tests mounting provider consumers to assert composable behavior and ACT/PING payloads; type tests verify parameter narrowing and return types.
Documentation
packages/vue/README.md
Usage docs and example showing createAbby config, AbbyProvider wrapping, and composable examples.
Version Metadata
.changeset/clean-vue-integration.md
Changeset records a minor version bump and describes the added Vue integration package with typed composables.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add Vue integration package' directly matches the PR's primary objective: introducing a new @tryabby/vue package with typed composables for Vue. It is concise, specific, and clearly summarizes the main change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/vue/tests/createAbby.test.ts (1)

116-124: ⚡ Quick win

Use AbbyEventType.ACT instead of the literal 1.

Coupling the assertion to the numeric value of the enum makes the test fragile to reordering of AbbyEventType members in @tryabby/core and obscures intent at the call site.

♻️ Proposed change
-import { HttpService } from "@tryabby/core";
+import { AbbyEventType, HttpService } from "@tryabby/core";
@@
-    expect(sendData).toHaveBeenCalledWith(
-      expect.objectContaining({
-        type: 1,
-        data: expect.objectContaining({
-          projectId: "project",
-          testName: "buttonCopy",
-        }),
-      })
-    );
+    expect(sendData).toHaveBeenCalledWith(
+      expect.objectContaining({
+        type: AbbyEventType.ACT,
+        data: expect.objectContaining({
+          projectId: "project",
+          testName: "buttonCopy",
+        }),
+      })
+    );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/vue/tests/createAbby.test.ts` around lines 116 - 124, The assertion
uses the numeric literal 1 for the event type which is fragile; import
AbbyEventType from "@tryabby/core" and replace the literal with
AbbyEventType.ACT in the expect in createAbby.test.ts (the expect(... sendData
... objectContaining { type: ... } block) so the test asserts using the enum
value instead of the numeric literal, improving clarity and stability.
packages/vue/src/StorageService.ts (1)

12-67: ⚡ Quick win

Consolidate three near-identical cookie storage classes.

The three classes are virtually identical apart from the storage-key function. A single generic class parameterized by the key builder would eliminate duplication and reduce future maintenance cost (e.g., changing cookie attributes/SameSite/Secure would currently need to be done in three places).

♻️ Proposed refactor
-class ABStorageService implements IStorageService {
-  get(projectId: string, testName: string): string | null {
-    return Cookie.get(getABStorageKey(projectId, testName)) ?? null;
-  }
-
-  set(
-    projectId: string,
-    testName: string,
-    value: string,
-    options?: StorageServiceOptions
-  ): void {
-    Cookie.set(getABStorageKey(projectId, testName), value, {
-      expires: options?.expiresInDays ?? DEFAULT_COOKIE_AGE,
-    });
-  }
-
-  remove(projectId: string, testName: string): void {
-    Cookie.remove(getABStorageKey(projectId, testName));
-  }
-}
-
-class FFStorageService implements IStorageService {
-  get(projectId: string, flagName: string): string | null {
-    return Cookie.get(getFFStorageKey(projectId, flagName)) ?? null;
-  }
-
-  set(projectId: string, flagName: string, value: string): void {
-    Cookie.set(getFFStorageKey(projectId, flagName), value, {
-      expires: DEFAULT_COOKIE_AGE,
-    });
-  }
-
-  remove(projectId: string, flagName: string): void {
-    Cookie.remove(getFFStorageKey(projectId, flagName));
-  }
-}
-
-class RCStorageService implements IStorageService {
-  get(projectId: string, key: string): string | null {
-    return Cookie.get(getRCStorageKey(projectId, key)) ?? null;
-  }
-
-  set(projectId: string, key: string, value: string): void {
-    Cookie.set(getRCStorageKey(projectId, key), value, {
-      expires: DEFAULT_COOKIE_AGE,
-    });
-  }
-
-  remove(projectId: string, key: string): void {
-    Cookie.remove(getRCStorageKey(projectId, key));
-  }
-}
-
-export const TestStorageService = new ABStorageService();
-export const FlagStorageService = new FFStorageService();
-export const RemoteConfigStorageService = new RCStorageService();
+class CookieStorageService implements IStorageService {
+  constructor(
+    private readonly buildKey: (projectId: string, name: string) => string
+  ) {}
+
+  get(projectId: string, name: string): string | null {
+    return Cookie.get(this.buildKey(projectId, name)) ?? null;
+  }
+
+  set(
+    projectId: string,
+    name: string,
+    value: string,
+    options?: StorageServiceOptions
+  ): void {
+    Cookie.set(this.buildKey(projectId, name), value, {
+      expires: options?.expiresInDays ?? DEFAULT_COOKIE_AGE,
+    });
+  }
+
+  remove(projectId: string, name: string): void {
+    Cookie.remove(this.buildKey(projectId, name));
+  }
+}
+
+export const TestStorageService = new CookieStorageService(getABStorageKey);
+export const FlagStorageService = new CookieStorageService(getFFStorageKey);
+export const RemoteConfigStorageService = new CookieStorageService(getRCStorageKey);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/vue/src/StorageService.ts` around lines 12 - 67, The three nearly
identical classes ABStorageService, FFStorageService, and RCStorageService
should be replaced by a single generic implementation (e.g.,
GenericCookieStorage) that implements IStorageService and accepts a keyBuilder
function (the existing getABStorageKey, getFFStorageKey, getRCStorageKey) in its
constructor; implement get/set/remove to call keyBuilder(projectId, name) and
use the same cookie options (honoring options?.expiresInDays in set), then
export TestStorageService, FlagStorageService, and RemoteConfigStorageService as
instances of GenericCookieStorage constructed with the corresponding key
builders so future cookie attribute changes are made in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/vue/src/index.ts`:
- Around line 149-152: The subscription to abby.subscribe is being created
during setup (which runs in SSR) and cleaned up with onBeforeUnmount (which
doesn't run on the server), causing per-request leaks; move the
subscribe/unsubscribe pair into onMounted so the subscription is only created on
the client: inside setup keep the data ref, but call abby.subscribe(...) and
register the returned unsubscribe inside onMounted; remove/avoid creating the
subscription at top-level of setup and ensure the onBeforeUnmount cleanup
remains paired with the same unsubscribe reference created in onMounted.

---

Nitpick comments:
In `@packages/vue/src/StorageService.ts`:
- Around line 12-67: The three nearly identical classes ABStorageService,
FFStorageService, and RCStorageService should be replaced by a single generic
implementation (e.g., GenericCookieStorage) that implements IStorageService and
accepts a keyBuilder function (the existing getABStorageKey, getFFStorageKey,
getRCStorageKey) in its constructor; implement get/set/remove to call
keyBuilder(projectId, name) and use the same cookie options (honoring
options?.expiresInDays in set), then export TestStorageService,
FlagStorageService, and RemoteConfigStorageService as instances of
GenericCookieStorage constructed with the corresponding key builders so future
cookie attribute changes are made in one place.

In `@packages/vue/tests/createAbby.test.ts`:
- Around line 116-124: The assertion uses the numeric literal 1 for the event
type which is fragile; import AbbyEventType from "@tryabby/core" and replace the
literal with AbbyEventType.ACT in the expect in createAbby.test.ts (the
expect(... sendData ... objectContaining { type: ... } block) so the test
asserts using the enum value instead of the numeric literal, improving clarity
and stability.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 000a83b6-8ea7-4be2-8d84-e83a054a9d18

📥 Commits

Reviewing files that changed from the base of the PR and between e763d1a and ef1529f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • .changeset/clean-vue-integration.md
  • packages/vue/README.md
  • packages/vue/package.json
  • packages/vue/src/StorageService.ts
  • packages/vue/src/index.ts
  • packages/vue/tests/createAbby.test.ts
  • packages/vue/tsconfig.json
  • packages/vue/vitest.config.ts

Comment thread packages/vue/src/index.ts Outdated
@Iineman2
Copy link
Copy Markdown
Author

Addressed the CodeRabbit SSR lifecycle feedback in 75b5a26: the provider now creates the Abby subscription only after client mount and keeps the cleanup registered through the component unmount hook. Also replaced the magic ACT event number in the focused test with the core enum.\n\nVerification:\n- pnpm --filter @tryabby/vue exec vitest run\n- pnpm --filter @tryabby/vue build\n- pnpm exec biome check packages/vue/src packages/vue/tests packages/vue/vitest.config.ts packages/vue/README.md

@Iineman2
Copy link
Copy Markdown
Author

Polished the remaining storage-service nit in aee61aa by replacing the three duplicate cookie storage classes with a single keyed implementation while preserving the exported services and expiration behavior.\n\nVerification after the update:\n- \pnpm --filter @tryabby/vue exec vitest run\n- \pnpm --filter @tryabby/vue build\n- \pnpm exec biome check packages/vue/src packages/vue/tests packages/vue/vitest.config.ts packages/vue/README.md\n\nAll passed locally. The only local note was the existing Node engine warning because this machine is on Node v20.19.4 while the repo asks for >=22.8.0.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/vue/src/StorageService.ts`:
- Around line 17-19: The StorageService methods lack an explicit SSR guard;
update get (StorageService.get), set (StorageService.set) and remove
(StorageService.remove) to check if typeof window === "undefined" before using
Cookie: have get return null immediately when window is undefined, and have
set/remove be no-ops (return void) when window is undefined, while still using
this.buildKey(projectId, name) for keys when running in the browser; keep
existing Cookie usage unchanged for the browser path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 46cac539-6518-447c-9235-cca1e1a7688c

📥 Commits

Reviewing files that changed from the base of the PR and between ef1529f and aee61aa.

📒 Files selected for processing (3)
  • packages/vue/src/StorageService.ts
  • packages/vue/src/index.ts
  • packages/vue/tests/createAbby.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/vue/tests/createAbby.test.ts
  • packages/vue/src/index.ts

Comment thread packages/vue/src/StorageService.ts
@Iineman2
Copy link
Copy Markdown
Author

Addressed the follow-up SSR storage guard feedback in c0d06cd: the shared Vue cookie storage now returns null for reads and no-ops writes/removes when running without a browser window, while preserving the browser cookie path unchanged.\n\nVerification after the update:\n- \pnpm --filter @tryabby/vue exec vitest run\n- \pnpm --filter @tryabby/vue build\n- \pnpm exec biome check packages/vue/src packages/vue/tests packages/vue/vitest.config.ts packages/vue/README.md\n\nAll passed locally. Same local environment note as before: Node v20.19.4 reports the repo's >=22.8.0 engine warning.

@Iineman2
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Iineman2
Copy link
Copy Markdown
Author

Added a short package walkthrough video for the bounty/review evidence: https://github.com/Iineman2/abby/releases/download/abby-pr181-vue-package-demo/abby-pr181-vue-package-demo.mp4

Current maintenance status from my side: the CodeRabbit feedback has been addressed, local package tests/build/biome passed as noted above, and the remaining failing Vercel status appears to be the usual fork deployment authorization gate requiring a team member to approve or ignore the preview.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants