@avsbhq/vue 1.0.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/dist/index.js ADDED
@@ -0,0 +1,519 @@
1
+ // src/AvsbProvider.ts
2
+ import {
3
+ defineComponent,
4
+ ref,
5
+ provide,
6
+ onMounted,
7
+ onUnmounted,
8
+ h
9
+ } from "vue";
10
+ import { AvsbClient } from "@avsbhq/browser";
11
+
12
+ // src/context.ts
13
+ import { inject } from "vue";
14
+ var AvsbInjectionKey = /* @__PURE__ */ Symbol("avsb-injection-key");
15
+ function useAvsbContext() {
16
+ const v = inject(AvsbInjectionKey);
17
+ if (v === void 0) {
18
+ throw new Error(
19
+ '[avsb] composables must be used inside <AvsbProvider> or app.use(AvsbPlugin). Wrap your component tree with <AvsbProvider sdkKey="..." context={...}> or install the plugin.'
20
+ );
21
+ }
22
+ return v;
23
+ }
24
+
25
+ // src/AvsbProvider.ts
26
+ var AvsbProvider = defineComponent({
27
+ name: "AvsbProvider",
28
+ props: {
29
+ // Mode B — caller passes a pre-built client
30
+ client: {
31
+ type: Object,
32
+ default: void 0
33
+ },
34
+ // Mode A — sdkKey is the minimum required field from AvsbClientOptions
35
+ sdkKey: {
36
+ type: String,
37
+ default: void 0
38
+ }
39
+ },
40
+ // Accept any additional option props (pollingInterval, bootstrap, etc.)
41
+ // via inheritAttrs + $attrs so callers can pass the full AvsbClientOptions
42
+ // surface without enumerating every field as a prop.
43
+ inheritAttrs: false,
44
+ setup(props, { slots, attrs }) {
45
+ const injectedClient = props.client ?? null;
46
+ const isInjectionMode = injectedClient !== null;
47
+ let ownedClient = null;
48
+ if (!isInjectionMode) {
49
+ if (!props.sdkKey) {
50
+ throw new Error(
51
+ "[avsb] <AvsbProvider> requires either a `client` prop (Mode B) or a `sdkKey` prop (Mode A)."
52
+ );
53
+ }
54
+ const opts = {
55
+ sdkKey: props.sdkKey,
56
+ ...attrs
57
+ };
58
+ ownedClient = new AvsbClient(opts);
59
+ }
60
+ const activeClient = isInjectionMode ? injectedClient : ownedClient;
61
+ const status = ref("loading");
62
+ const error = ref(void 0);
63
+ provide(AvsbInjectionKey, {
64
+ get client() {
65
+ return activeClient;
66
+ },
67
+ get status() {
68
+ return status.value;
69
+ },
70
+ get error() {
71
+ return error.value;
72
+ }
73
+ });
74
+ let cancelled = false;
75
+ let unsubscribeError;
76
+ onMounted(() => {
77
+ if (!activeClient) return;
78
+ activeClient.onReady().then(() => {
79
+ if (!cancelled) status.value = "ready";
80
+ }).catch((err) => {
81
+ if (!cancelled) {
82
+ status.value = "error";
83
+ error.value = err instanceof Error ? err : new Error(String(err));
84
+ }
85
+ });
86
+ if (typeof activeClient.on === "function") {
87
+ unsubscribeError = activeClient.on("error", (e) => {
88
+ if (!cancelled) {
89
+ status.value = "error";
90
+ error.value = e;
91
+ }
92
+ });
93
+ }
94
+ });
95
+ onUnmounted(() => {
96
+ cancelled = true;
97
+ unsubscribeError?.();
98
+ if (!isInjectionMode && ownedClient) {
99
+ ownedClient.close().catch(() => {
100
+ });
101
+ ownedClient = null;
102
+ }
103
+ });
104
+ return () => slots.default?.();
105
+ },
106
+ render() {
107
+ return h("div");
108
+ }
109
+ });
110
+
111
+ // src/plugin.ts
112
+ import { ref as ref2 } from "vue";
113
+ import { AvsbClient as AvsbClient2 } from "@avsbhq/browser";
114
+ var AvsbPlugin = {
115
+ install(app, options) {
116
+ const injectedClient = "client" in options && options.client ? options.client : null;
117
+ const isInjectionMode = injectedClient !== null;
118
+ let activeClient;
119
+ if (isInjectionMode) {
120
+ activeClient = injectedClient;
121
+ } else {
122
+ const opts = options;
123
+ activeClient = new AvsbClient2(opts);
124
+ }
125
+ const status = ref2("loading");
126
+ const error = ref2(void 0);
127
+ app.provide(AvsbInjectionKey, {
128
+ get client() {
129
+ return activeClient;
130
+ },
131
+ get status() {
132
+ return status.value;
133
+ },
134
+ get error() {
135
+ return error.value;
136
+ }
137
+ });
138
+ activeClient.onReady().then(() => {
139
+ status.value = "ready";
140
+ }).catch((err) => {
141
+ status.value = "error";
142
+ error.value = err instanceof Error ? err : new Error(String(err));
143
+ });
144
+ if (typeof activeClient.on === "function") {
145
+ activeClient.on("error", (e) => {
146
+ status.value = "error";
147
+ error.value = e;
148
+ });
149
+ }
150
+ if (!isInjectionMode) {
151
+ app.unmount = new Proxy(app.unmount, {
152
+ apply(target, thisArg, args) {
153
+ activeClient.close().catch(() => {
154
+ });
155
+ return Reflect.apply(target, thisArg, args);
156
+ }
157
+ });
158
+ }
159
+ }
160
+ };
161
+
162
+ // src/composables/useFlag.ts
163
+ import {
164
+ ref as ref3,
165
+ computed,
166
+ onMounted as onMounted2,
167
+ onUnmounted as onUnmounted2,
168
+ watchEffect,
169
+ isRef
170
+ } from "vue";
171
+ import { notFoundFlag } from "@avsbhq/core";
172
+ function useFlag(flagKey, defaultValue) {
173
+ const { client } = useAvsbContext();
174
+ const noClientSentinel = notFoundFlag(defaultValue, "no client");
175
+ const flagRef = ref3(
176
+ client !== null && typeof client.getSnapshot === "function" ? client.getSnapshot(
177
+ isRef(flagKey) ? flagKey.value : flagKey,
178
+ defaultValue
179
+ ) : noClientSentinel
180
+ );
181
+ let unsub;
182
+ function resubscribe() {
183
+ unsub?.();
184
+ unsub = void 0;
185
+ if (!client || typeof client.subscribe !== "function") return;
186
+ const key = isRef(flagKey) ? flagKey.value : flagKey;
187
+ if (typeof client.getSnapshot === "function") {
188
+ flagRef.value = client.getSnapshot(key, defaultValue);
189
+ }
190
+ unsub = client.subscribe(key, () => {
191
+ if (typeof client.getSnapshot === "function") {
192
+ flagRef.value = client.getSnapshot(
193
+ isRef(flagKey) ? flagKey.value : flagKey,
194
+ defaultValue
195
+ );
196
+ }
197
+ });
198
+ }
199
+ let stopWatcher;
200
+ onMounted2(() => {
201
+ if (isRef(flagKey)) {
202
+ stopWatcher = watchEffect(() => {
203
+ void flagKey.value;
204
+ resubscribe();
205
+ });
206
+ } else {
207
+ resubscribe();
208
+ }
209
+ });
210
+ onUnmounted2(() => {
211
+ stopWatcher?.();
212
+ unsub?.();
213
+ });
214
+ return computed(() => flagRef.value);
215
+ }
216
+
217
+ // src/composables/useFlagValue.ts
218
+ import { computed as computed2 } from "vue";
219
+ function useFlagValue(flagKey, defaultValue) {
220
+ const flag = useFlag(flagKey, defaultValue);
221
+ return computed2(() => flag.value.value);
222
+ }
223
+
224
+ // src/composables/useBoolFlag.ts
225
+ import {
226
+ ref as ref4,
227
+ computed as computed3,
228
+ onMounted as onMounted3,
229
+ onUnmounted as onUnmounted3
230
+ } from "vue";
231
+ import { notFoundFlag as notFoundFlag2 } from "@avsbhq/core";
232
+ function useBoolFlag(flagKey, defaultValue) {
233
+ const { client } = useAvsbContext();
234
+ const noClientSentinel = notFoundFlag2(defaultValue, "no client");
235
+ const flagRef = ref4(
236
+ client !== null && typeof client.getBoolFlag === "function" ? client.getBoolFlag(flagKey, defaultValue) : noClientSentinel
237
+ );
238
+ let unsub;
239
+ onMounted3(() => {
240
+ if (!client || typeof client.subscribe !== "function") return;
241
+ if (typeof client.getBoolFlag === "function") {
242
+ flagRef.value = client.getBoolFlag(flagKey, defaultValue);
243
+ }
244
+ unsub = client.subscribe(flagKey, () => {
245
+ if (typeof client.getBoolFlag === "function") {
246
+ flagRef.value = client.getBoolFlag(flagKey, defaultValue);
247
+ }
248
+ });
249
+ });
250
+ onUnmounted3(() => {
251
+ unsub?.();
252
+ });
253
+ return computed3(() => flagRef.value);
254
+ }
255
+
256
+ // src/composables/useStringFlag.ts
257
+ import {
258
+ ref as ref5,
259
+ computed as computed4,
260
+ onMounted as onMounted4,
261
+ onUnmounted as onUnmounted4
262
+ } from "vue";
263
+ import { notFoundFlag as notFoundFlag3 } from "@avsbhq/core";
264
+ function useStringFlag(flagKey, defaultValue) {
265
+ const { client } = useAvsbContext();
266
+ const noClientSentinel = notFoundFlag3(defaultValue, "no client");
267
+ const flagRef = ref5(
268
+ client !== null && typeof client.getStringFlag === "function" ? client.getStringFlag(flagKey, defaultValue) : noClientSentinel
269
+ );
270
+ let unsub;
271
+ onMounted4(() => {
272
+ if (!client || typeof client.subscribe !== "function") return;
273
+ if (typeof client.getStringFlag === "function") {
274
+ flagRef.value = client.getStringFlag(flagKey, defaultValue);
275
+ }
276
+ unsub = client.subscribe(flagKey, () => {
277
+ if (typeof client.getStringFlag === "function") {
278
+ flagRef.value = client.getStringFlag(flagKey, defaultValue);
279
+ }
280
+ });
281
+ });
282
+ onUnmounted4(() => {
283
+ unsub?.();
284
+ });
285
+ return computed4(() => flagRef.value);
286
+ }
287
+
288
+ // src/composables/useNumberFlag.ts
289
+ import {
290
+ ref as ref6,
291
+ computed as computed5,
292
+ onMounted as onMounted5,
293
+ onUnmounted as onUnmounted5
294
+ } from "vue";
295
+ import { notFoundFlag as notFoundFlag4 } from "@avsbhq/core";
296
+ function useNumberFlag(flagKey, defaultValue) {
297
+ const { client } = useAvsbContext();
298
+ const noClientSentinel = notFoundFlag4(defaultValue, "no client");
299
+ const flagRef = ref6(
300
+ client !== null && typeof client.getNumberFlag === "function" ? client.getNumberFlag(flagKey, defaultValue) : noClientSentinel
301
+ );
302
+ let unsub;
303
+ onMounted5(() => {
304
+ if (!client || typeof client.subscribe !== "function") return;
305
+ if (typeof client.getNumberFlag === "function") {
306
+ flagRef.value = client.getNumberFlag(flagKey, defaultValue);
307
+ }
308
+ unsub = client.subscribe(flagKey, () => {
309
+ if (typeof client.getNumberFlag === "function") {
310
+ flagRef.value = client.getNumberFlag(flagKey, defaultValue);
311
+ }
312
+ });
313
+ });
314
+ onUnmounted5(() => {
315
+ unsub?.();
316
+ });
317
+ return computed5(() => flagRef.value);
318
+ }
319
+
320
+ // src/composables/useJsonFlag.ts
321
+ import {
322
+ ref as ref7,
323
+ computed as computed6,
324
+ onMounted as onMounted6,
325
+ onUnmounted as onUnmounted6
326
+ } from "vue";
327
+ import { notFoundFlag as notFoundFlag5 } from "@avsbhq/core";
328
+ function useJsonFlag(flagKey, defaultValue) {
329
+ const { client } = useAvsbContext();
330
+ const noClientSentinel = notFoundFlag5(defaultValue, "no client");
331
+ const flagRef = ref7(
332
+ client !== null && typeof client.getJsonFlag === "function" ? client.getJsonFlag(flagKey, defaultValue) : noClientSentinel
333
+ );
334
+ let unsub;
335
+ onMounted6(() => {
336
+ if (!client || typeof client.subscribe !== "function") return;
337
+ if (typeof client.getJsonFlag === "function") {
338
+ flagRef.value = client.getJsonFlag(flagKey, defaultValue);
339
+ }
340
+ unsub = client.subscribe(flagKey, () => {
341
+ if (typeof client.getJsonFlag === "function") {
342
+ flagRef.value = client.getJsonFlag(flagKey, defaultValue);
343
+ }
344
+ });
345
+ });
346
+ onUnmounted6(() => {
347
+ unsub?.();
348
+ });
349
+ return computed6(() => flagRef.value);
350
+ }
351
+
352
+ // src/composables/useAllFlags.ts
353
+ import {
354
+ ref as ref8,
355
+ computed as computed7,
356
+ onMounted as onMounted7,
357
+ onUnmounted as onUnmounted7
358
+ } from "vue";
359
+ function useAllFlags() {
360
+ const { client } = useAvsbContext();
361
+ const snapshotRef = ref8(
362
+ client !== null && typeof client.getAllFlags === "function" ? client.getAllFlags() : {}
363
+ );
364
+ let unsub;
365
+ onMounted7(() => {
366
+ if (!client || typeof client.on !== "function") return;
367
+ if (typeof client.getAllFlags === "function") {
368
+ snapshotRef.value = client.getAllFlags();
369
+ }
370
+ unsub = client.on("flagChange", () => {
371
+ if (typeof client.getAllFlags === "function") {
372
+ snapshotRef.value = client.getAllFlags();
373
+ }
374
+ });
375
+ });
376
+ onUnmounted7(() => {
377
+ unsub?.();
378
+ });
379
+ return computed7(() => snapshotRef.value);
380
+ }
381
+
382
+ // src/composables/useFlagReady.ts
383
+ import { computed as computed8 } from "vue";
384
+ function useFlagReady() {
385
+ const context = useAvsbContext();
386
+ return computed8(() => context.status === "ready");
387
+ }
388
+
389
+ // src/composables/useAvsbClient.ts
390
+ function useAvsbClient() {
391
+ const { client } = useAvsbContext();
392
+ return client;
393
+ }
394
+
395
+ // src/composables/useIdentify.ts
396
+ import { ref as ref9 } from "vue";
397
+ function useIdentify() {
398
+ const { client } = useAvsbContext();
399
+ const clientRef = ref9(client);
400
+ clientRef.value = client;
401
+ function identify(ctx) {
402
+ const c = clientRef.value;
403
+ if (!c || typeof c.identify !== "function") return;
404
+ c.identify(ctx);
405
+ }
406
+ return identify;
407
+ }
408
+
409
+ // src/composables/useAlias.ts
410
+ import { ref as ref10 } from "vue";
411
+ function useAlias() {
412
+ const { client } = useAvsbContext();
413
+ const clientRef = ref10(client);
414
+ clientRef.value = client;
415
+ async function alias(prev, next) {
416
+ const c = clientRef.value;
417
+ if (!c || typeof c.alias !== "function") return;
418
+ await c.alias(prev, next);
419
+ }
420
+ return alias;
421
+ }
422
+
423
+ // src/composables/useTrack.ts
424
+ import { ref as ref11 } from "vue";
425
+ function useTrack() {
426
+ const { client } = useAvsbContext();
427
+ const clientRef = ref11(client);
428
+ clientRef.value = client;
429
+ function track(eventKey, payload) {
430
+ const c = clientRef.value;
431
+ if (!c || typeof c.track !== "function") return;
432
+ c.track(eventKey, payload);
433
+ }
434
+ return track;
435
+ }
436
+
437
+ // src/pinia.ts
438
+ import { defineStore } from "pinia";
439
+ import { ref as ref12, computed as computed9 } from "vue";
440
+ import { inject as inject2 } from "vue";
441
+ var useAvsbStore = defineStore("avsb", () => {
442
+ const injectedContext = inject2(AvsbInjectionKey);
443
+ const _client = ref12(injectedContext?.client ?? null);
444
+ const _status = ref12(
445
+ injectedContext?.status ?? "loading"
446
+ );
447
+ const _error = ref12(injectedContext?.error);
448
+ if (injectedContext) {
449
+ }
450
+ const client = computed9(
451
+ () => injectedContext ? injectedContext.client : _client.value
452
+ );
453
+ const status = computed9(
454
+ () => injectedContext ? injectedContext.status : _status.value
455
+ );
456
+ const error = computed9(
457
+ () => injectedContext ? injectedContext.error : _error.value
458
+ );
459
+ function setClient(c) {
460
+ _client.value = c;
461
+ _status.value = "loading";
462
+ _error.value = void 0;
463
+ if (!c) return;
464
+ c.onReady().then(() => {
465
+ _status.value = "ready";
466
+ }).catch((err) => {
467
+ _status.value = "error";
468
+ _error.value = err instanceof Error ? err : new Error(String(err));
469
+ });
470
+ }
471
+ function identify(ctx) {
472
+ const c = client.value;
473
+ if (!c || typeof c.identify !== "function") return;
474
+ c.identify(ctx);
475
+ }
476
+ function track(eventKey, payload) {
477
+ const c = client.value;
478
+ if (!c || typeof c.track !== "function") return;
479
+ c.track(eventKey, payload);
480
+ }
481
+ async function alias(prev, next) {
482
+ const c = client.value;
483
+ if (!c || typeof c.alias !== "function") return;
484
+ await c.alias(prev, next);
485
+ }
486
+ function reset() {
487
+ const c = client.value;
488
+ if (!c || typeof c.reset !== "function") return;
489
+ c.reset();
490
+ }
491
+ return {
492
+ client,
493
+ status,
494
+ error,
495
+ setClient,
496
+ identify,
497
+ track,
498
+ alias,
499
+ reset
500
+ };
501
+ });
502
+ export {
503
+ AvsbInjectionKey,
504
+ AvsbPlugin,
505
+ AvsbProvider,
506
+ useAlias,
507
+ useAllFlags,
508
+ useAvsbClient,
509
+ useAvsbStore,
510
+ useBoolFlag,
511
+ useFlag,
512
+ useFlagReady,
513
+ useFlagValue,
514
+ useIdentify,
515
+ useJsonFlag,
516
+ useNumberFlag,
517
+ useStringFlag,
518
+ useTrack
519
+ };
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@avsbhq/vue",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "main": "dist/index.cjs",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "files": ["dist"],
16
+ "publishConfig": { "access": "public" },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/main1479/a-vs-b.git",
20
+ "directory": "packages/avsb-vue"
21
+ },
22
+ "homepage": "https://github.com/main1479/a-vs-b/tree/main/packages/avsb-vue#readme",
23
+ "peerDependencies": {
24
+ "vue": ">=3.4.0"
25
+ },
26
+ "peerDependenciesMeta": {
27
+ "pinia": { "optional": true }
28
+ },
29
+ "dependencies": {
30
+ "@avsbhq/core": "1.0.0",
31
+ "@avsbhq/browser": "1.0.0"
32
+ },
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "dev": "tsup --watch",
36
+ "test": "vitest run",
37
+ "typecheck": "tsc --noEmit"
38
+ },
39
+ "devDependencies": {
40
+ "@vitejs/plugin-vue": "^5.0.0",
41
+ "@vue/test-utils": "^2.4.0",
42
+ "happy-dom": "^14.0.0",
43
+ "pinia": "^2.1.0",
44
+ "tsup": "^8.0.0",
45
+ "typescript": "^5.5.0",
46
+ "vitest": "^1.6.0",
47
+ "vue": "^3.4.0"
48
+ }
49
+ }