@fingerru/vue 1.0.0-next.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 ADDED
@@ -0,0 +1,157 @@
1
+ # @fingerru/vue
2
+
3
+ Vue 3-обёртка над `@fingerru/agent`. Подключите плагин один раз при создании
4
+ приложения и получайте данные visitor через Composition API или Options API.
5
+
6
+ ## Установка
7
+
8
+ ```bash
9
+ pnpm add @fingerru/vue @fingerru/agent
10
+ ```
11
+
12
+ ## Подключение плагина
13
+
14
+ Создайте Fingprint-плагин при инициализации приложения:
15
+
16
+ ```ts
17
+ import { createApp } from 'vue'
18
+ import { createFingprint } from '@fingerru/vue'
19
+ import App from './App.vue'
20
+
21
+ createApp(App)
22
+ .use(
23
+ createFingprint({
24
+ apiKey: 'pk_live_example',
25
+ }),
26
+ )
27
+ .mount('#app')
28
+ ```
29
+
30
+ `endpoint` — полный абсолютный URL API или proxy. Если его не передавать,
31
+ `@fingerru/agent` использует URL по умолчанию, встроенный в bundle.
32
+
33
+ ```ts
34
+ createFingprint({
35
+ apiKey: 'pk_live_example',
36
+ endpoint: 'https://api.example.com/api/visit',
37
+ runtimeUrl: 'https://cdn.example.com/agent.min.js',
38
+ loadTimeout: 10_000,
39
+ cache: {
40
+ storage: 'sessionStorage',
41
+ duration: 3600,
42
+ },
43
+ })
44
+ ```
45
+
46
+ ## Composition API
47
+
48
+ `useVisitorData()` доступен в компонентах, которые находятся внутри приложения
49
+ с подключённым `createFingprint()`. Он возвращает реактивные refs `data`,
50
+ `error`, `isLoading` и `isFetched`, а также метод `getData()`.
51
+
52
+ ```vue
53
+ <script setup lang="ts">
54
+ import { useVisitorData } from '@fingerru/vue'
55
+ import { watch } from 'vue'
56
+
57
+ const { data, error, isFetched, isLoading, getData } = useVisitorData({
58
+ // Укажите true, если идентификация нужна сразу после mount.
59
+ immediate: false,
60
+ })
61
+
62
+ watch(data, (visitor) => {
63
+ if (visitor) {
64
+ console.log(visitor.user)
65
+ }
66
+ })
67
+ </script>
68
+
69
+ <template>
70
+ <button type="button" :disabled="isLoading" @click="getData()">Получить visitor</button>
71
+
72
+ <p v-if="isLoading">Получаем visitor…</p>
73
+ <p v-else-if="error">{{ error.message }}</p>
74
+ <p v-else-if="isFetched">Visitor: {{ data?.user }}</p>
75
+ </template>
76
+ ```
77
+
78
+ В `<template>` Vue автоматически разворачивает refs. В обычном TypeScript-коде
79
+ используйте `.value`, например `data.value?.user`.
80
+
81
+ По умолчанию запрос не запускается автоматически. Передайте
82
+ `{ immediate: true }`, чтобы выполнить его после монтирования компонента.
83
+ Повторные вызовы `getData()` одного composable во время активного запроса
84
+ получают один и тот же `Promise`.
85
+
86
+ ### Параметры запроса
87
+
88
+ Параметры можно указать при создании composable или передать в конкретный
89
+ вызов. Параметры вызова имеют приоритет:
90
+
91
+ ```ts
92
+ const { getData } = useVisitorData({ timeout: 5_000 })
93
+
94
+ // Запрашивает новые данные, игнорируя cache.
95
+ const visitor = await getData({ ignoreCache: true })
96
+ ```
97
+
98
+ | Параметр | Описание |
99
+ | ------------- | ------------------------------------------------------- |
100
+ | `immediate` | Выполнить запрос после mount. По умолчанию — `false`. |
101
+ | `timeout` | Максимальное время API-запроса в миллисекундах. |
102
+ | `ignoreCache` | Не использовать сохранённый результат для этого вызова. |
103
+
104
+ ## Options API
105
+
106
+ После подключения плагин добавляет типизированный `$fingprint` во все
107
+ компоненты. Через него доступен `getVisitorData()`:
108
+
109
+ ```vue
110
+ <script lang="ts">
111
+ import { defineComponent } from 'vue'
112
+
113
+ export default defineComponent({
114
+ methods: {
115
+ async getVisitorData(): Promise<void> {
116
+ const visitor = await this.$fingprint.getVisitorData()
117
+ console.log(visitor.user)
118
+ },
119
+ },
120
+ })
121
+ </script>
122
+
123
+ <template>
124
+ <button type="button" @click="getVisitorData">Получить visitor</button>
125
+ </template>
126
+ ```
127
+
128
+ ## Nuxt
129
+
130
+ Fingerprinting использует browser runtime, поэтому подключайте плагин только
131
+ на клиенте. Создайте `plugins/fingprint.client.ts`:
132
+
133
+ ```ts
134
+ import { createFingprint } from '@fingerru/vue'
135
+
136
+ export default defineNuxtPlugin((nuxtApp) => {
137
+ const config = useRuntimeConfig()
138
+
139
+ nuxtApp.vueApp.use(
140
+ createFingprint({
141
+ apiKey: config.public.fingprintApiKey,
142
+ }),
143
+ )
144
+ })
145
+ ```
146
+
147
+ Публичный ключ передавайте через публичную runtime-конфигурацию:
148
+
149
+ ```ts
150
+ export default defineNuxtConfig({
151
+ runtimeConfig: {
152
+ public: {
153
+ fingprintApiKey: process.env.FINGPRINT_API_KEY,
154
+ },
155
+ },
156
+ })
157
+ ```
package/dist/index.js ADDED
@@ -0,0 +1,69 @@
1
+ import { start as h } from "@fingerru/agent";
2
+ import { inject as m, shallowRef as F, computed as a, onMounted as L } from "vue";
3
+ const l = Symbol("fingprint"), y = (n) => {
4
+ const s = h(n), o = {
5
+ getVisitorData: (t) => s.get(t)
6
+ };
7
+ return {
8
+ install: (t) => {
9
+ t.provide(l, o), t.config.globalProperties.$fingprint = o;
10
+ }
11
+ };
12
+ }, D = {
13
+ data: void 0,
14
+ error: void 0,
15
+ isFetched: !1,
16
+ isLoading: !1
17
+ }, V = {
18
+ data: void 0,
19
+ error: void 0,
20
+ isFetched: !1,
21
+ isLoading: !0
22
+ }, x = ({
23
+ immediate: n,
24
+ timeout: s,
25
+ ignoreCache: o
26
+ } = {}) => {
27
+ const t = m(l);
28
+ if (!t)
29
+ throw new Error("useVisitorData must be used after app.use(createFingprint(...)).");
30
+ let i = null;
31
+ const e = F(D), u = a(() => e.value.data), g = a(() => e.value.error), f = a(() => e.value.isLoading), v = a(() => e.value.isFetched), d = (p) => {
32
+ if (i)
33
+ return i;
34
+ e.value = V;
35
+ const r = t.getVisitorData({ timeout: s, ignoreCache: o, ...p });
36
+ return i = r, r.then((c) => {
37
+ e.value = {
38
+ data: c,
39
+ error: void 0,
40
+ isFetched: !0,
41
+ isLoading: !1
42
+ };
43
+ }).catch((c) => {
44
+ e.value = {
45
+ data: void 0,
46
+ error: c,
47
+ isFetched: !1,
48
+ isLoading: !1
49
+ };
50
+ }).finally(() => {
51
+ i === r && (i = null);
52
+ }), r;
53
+ };
54
+ return L(() => {
55
+ n && d().catch(() => {
56
+ });
57
+ }), {
58
+ data: u,
59
+ error: g,
60
+ isLoading: f,
61
+ isFetched: v,
62
+ getData: d
63
+ };
64
+ };
65
+ export {
66
+ y as createFingprint,
67
+ x as useVisitorData
68
+ };
69
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/config.ts","../src/plugin.ts","../src/use-visitor-data.ts"],"sourcesContent":["import type { GetOptions, VisitorResult } from '@fingerru/agent'\nimport { type InjectionKey } from 'vue'\n\nexport interface FingprintContextValue {\n getVisitorData: (config?: GetOptions) => Promise<VisitorResult>\n}\n\nexport const pluginKey: InjectionKey<FingprintContextValue> = Symbol('fingprint')\n","import { start, type GetOptions, type StartOptions } from '@fingerru/agent'\nimport type { App } from 'vue'\nimport { pluginKey, type FingprintContextValue } from './config'\n\nexport type CreateFingprintOptions = StartOptions\n\ndeclare module 'vue' {\n interface ComponentCustomProperties {\n $fingprint: FingprintContextValue\n }\n}\n\nexport const createFingprint = (options: CreateFingprintOptions) => {\n const agent = start(options)\n\n const client = {\n getVisitorData: (getOptions?: GetOptions) => agent.get(getOptions),\n }\n\n return {\n install: (app: App) => {\n app.provide(pluginKey, client)\n app.config.globalProperties.$fingprint = client\n },\n }\n}\n","import { computed, inject, onMounted, shallowRef, type ComputedRef } from 'vue'\nimport { pluginKey } from './config'\nimport type { GetOptions, VisitorResult } from '@fingerru/agent'\nimport type { QueryIdle, QueryLoading, QueryResult } from './types'\n\ninterface UseVisitorDataConfig {\n /**\n * Если `true`, автоматически запрашивает данные посетителя после монтирования\n * компонента. По умолчанию запрос выполняется только при явном вызове `getData()`.\n */\n immediate?: boolean\n}\n\nexport type QueryResultRefs<TData, TError = Error> = {\n [K in keyof QueryResult<TData, TError>]: ComputedRef<QueryResult<TData, TError>[K]>\n}\n\nexport type UseVisitorDataOptions = GetOptions & UseVisitorDataConfig\n\nexport type UseVisitorDataReturn = QueryResultRefs<VisitorResult> & {\n /**\n * Выполняет запрос на идентификацию к серверу и возвращает данные посетителя.\n */\n getData: (options?: GetOptions) => Promise<VisitorResult>\n}\n\nconst idle: QueryIdle = {\n data: undefined,\n error: undefined,\n isFetched: false,\n isLoading: false,\n}\n\nconst loading: QueryLoading = {\n data: undefined,\n error: undefined,\n isFetched: false,\n isLoading: true,\n}\n\nexport const useVisitorData = ({\n immediate,\n timeout,\n ignoreCache,\n}: UseVisitorDataOptions = {}): UseVisitorDataReturn => {\n const context = inject(pluginKey)\n\n if (!context) {\n throw new Error('useVisitorData must be used after app.use(createFingprint(...)).')\n }\n\n let pendingPromise: Promise<VisitorResult> | null = null\n const result = shallowRef<QueryResult<VisitorResult>>(idle)\n const data = computed(() => result.value.data)\n const error = computed(() => result.value.error)\n const isLoading = computed(() => result.value.isLoading)\n const isFetched = computed(() => result.value.isFetched)\n\n const getData = (overrideOptions?: GetOptions): Promise<VisitorResult> => {\n if (pendingPromise) {\n return pendingPromise\n }\n\n result.value = loading\n\n const request = context.getVisitorData({ timeout, ignoreCache, ...overrideOptions })\n\n pendingPromise = request\n\n request\n .then((visitor) => {\n result.value = {\n data: visitor,\n error: undefined,\n isFetched: true,\n isLoading: false,\n }\n })\n .catch((error) => {\n result.value = {\n data: undefined,\n error: error,\n isFetched: false,\n isLoading: false,\n }\n })\n .finally(() => {\n if (pendingPromise === request) {\n pendingPromise = null\n }\n })\n\n return request\n }\n\n onMounted(() => {\n if (immediate) {\n getData().catch(() => undefined)\n }\n })\n\n return {\n data,\n error,\n isLoading,\n isFetched,\n getData,\n }\n}\n"],"names":["pluginKey","createFingprint","options","agent","start","client","getOptions","app","idle","loading","useVisitorData","immediate","timeout","ignoreCache","context","inject","pendingPromise","result","shallowRef","data","computed","error","isLoading","isFetched","getData","overrideOptions","request","visitor","onMounted"],"mappings":";;AAOO,MAAMA,IAAiD,OAAO,WAAW,GCKnEC,IAAkB,CAACC,MAAoC;AAClE,QAAMC,IAAQC,EAAMF,CAAO,GAErBG,IAAS;AAAA,IACb,gBAAgB,CAACC,MAA4BH,EAAM,IAAIG,CAAU;AAAA,EAAA;AAGnE,SAAO;AAAA,IACL,SAAS,CAACC,MAAa;AACrB,MAAAA,EAAI,QAAQP,GAAWK,CAAM,GAC7BE,EAAI,OAAO,iBAAiB,aAAaF;AAAA,IAC3C;AAAA,EAAA;AAEJ,GCCMG,IAAkB;AAAA,EACtB,MAAM;AAAA,EACN,OAAO;AAAA,EACP,WAAW;AAAA,EACX,WAAW;AACb,GAEMC,IAAwB;AAAA,EAC5B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,WAAW;AAAA,EACX,WAAW;AACb,GAEaC,IAAiB,CAAC;AAAA,EAC7B,WAAAC;AAAA,EACA,SAAAC;AAAA,EACA,aAAAC;AACF,IAA2B,OAA6B;AACtD,QAAMC,IAAUC,EAAOf,CAAS;AAEhC,MAAI,CAACc;AACH,UAAM,IAAI,MAAM,kEAAkE;AAGpF,MAAIE,IAAgD;AACpD,QAAMC,IAASC,EAAuCV,CAAI,GACpDW,IAAOC,EAAS,MAAMH,EAAO,MAAM,IAAI,GACvCI,IAAQD,EAAS,MAAMH,EAAO,MAAM,KAAK,GACzCK,IAAYF,EAAS,MAAMH,EAAO,MAAM,SAAS,GACjDM,IAAYH,EAAS,MAAMH,EAAO,MAAM,SAAS,GAEjDO,IAAU,CAACC,MAAyD;AACxE,QAAIT;AACF,aAAOA;AAGT,IAAAC,EAAO,QAAQR;AAEf,UAAMiB,IAAUZ,EAAQ,eAAe,EAAE,SAAAF,GAAS,aAAAC,GAAa,GAAGY,GAAiB;AAEnF,WAAAT,IAAiBU,GAEjBA,EACG,KAAK,CAACC,MAAY;AACjB,MAAAV,EAAO,QAAQ;AAAA,QACb,MAAMU;AAAA,QACN,OAAO;AAAA,QACP,WAAW;AAAA,QACX,WAAW;AAAA,MAAA;AAAA,IAEf,CAAC,EACA,MAAM,CAACN,MAAU;AAChB,MAAAJ,EAAO,QAAQ;AAAA,QACb,MAAM;AAAA,QACN,OAAOI;AAAAA,QACP,WAAW;AAAA,QACX,WAAW;AAAA,MAAA;AAAA,IAEf,CAAC,EACA,QAAQ,MAAM;AACb,MAAIL,MAAmBU,MACrBV,IAAiB;AAAA,IAErB,CAAC,GAEIU;AAAA,EACT;AAEA,SAAAE,EAAU,MAAM;AACd,IAAIjB,KACFa,EAAA,EAAU,MAAM,MAAA;AAAA,KAAe;AAAA,EAEnC,CAAC,GAEM;AAAA,IACL,MAAAL;AAAA,IACA,OAAAE;AAAA,IACA,WAAAC;AAAA,IACA,WAAAC;AAAA,IACA,SAAAC;AAAA,EAAA;AAEJ;"}
@@ -0,0 +1,6 @@
1
+ import { GetOptions, VisitorResult } from '@fingerru/agent';
2
+ import { InjectionKey } from 'vue';
3
+ export interface FingprintContextValue {
4
+ getVisitorData: (config?: GetOptions) => Promise<VisitorResult>;
5
+ }
6
+ export declare const pluginKey: InjectionKey<FingprintContextValue>;
@@ -0,0 +1,2 @@
1
+ export { createFingprint, type CreateFingprintOptions } from './plugin';
2
+ export { useVisitorData, type UseVisitorDataReturn, type UseVisitorDataOptions, } from './use-visitor-data';
@@ -0,0 +1,12 @@
1
+ import { StartOptions } from '@fingerru/agent';
2
+ import { App } from 'vue';
3
+ import { FingprintContextValue } from './config';
4
+ export type CreateFingprintOptions = StartOptions;
5
+ declare module 'vue' {
6
+ interface ComponentCustomProperties {
7
+ $fingprint: FingprintContextValue;
8
+ }
9
+ }
10
+ export declare const createFingprint: (options: CreateFingprintOptions) => {
11
+ install: (app: App) => void;
12
+ };
@@ -0,0 +1,25 @@
1
+ export type QueryError<TError = Error> = {
2
+ data: undefined;
3
+ isLoading: false;
4
+ isFetched: false;
5
+ error: TError;
6
+ };
7
+ export type QueryFetched<TData> = {
8
+ data: TData;
9
+ isLoading: false;
10
+ isFetched: true;
11
+ error: undefined;
12
+ };
13
+ export type QueryIdle = {
14
+ data: undefined;
15
+ isLoading: false;
16
+ isFetched: false;
17
+ error: undefined;
18
+ };
19
+ export type QueryLoading = {
20
+ data: undefined;
21
+ isLoading: true;
22
+ isFetched: false;
23
+ error: undefined;
24
+ };
25
+ export type QueryResult<TData, TError = Error> = QueryIdle | QueryLoading | QueryFetched<TData> | QueryError<TError>;
@@ -0,0 +1,22 @@
1
+ import { ComputedRef } from 'vue';
2
+ import { GetOptions, VisitorResult } from '@fingerru/agent';
3
+ import { QueryResult } from './types';
4
+ interface UseVisitorDataConfig {
5
+ /**
6
+ * Если `true`, автоматически запрашивает данные посетителя после монтирования
7
+ * компонента. По умолчанию запрос выполняется только при явном вызове `getData()`.
8
+ */
9
+ immediate?: boolean;
10
+ }
11
+ export type QueryResultRefs<TData, TError = Error> = {
12
+ [K in keyof QueryResult<TData, TError>]: ComputedRef<QueryResult<TData, TError>[K]>;
13
+ };
14
+ export type UseVisitorDataOptions = GetOptions & UseVisitorDataConfig;
15
+ export type UseVisitorDataReturn = QueryResultRefs<VisitorResult> & {
16
+ /**
17
+ * Выполняет запрос на идентификацию к серверу и возвращает данные посетителя.
18
+ */
19
+ getData: (options?: GetOptions) => Promise<VisitorResult>;
20
+ };
21
+ export declare const useVisitorData: ({ immediate, timeout, ignoreCache, }?: UseVisitorDataOptions) => UseVisitorDataReturn;
22
+ export {};
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@fingerru/vue",
3
+ "version": "1.0.0-next.0",
4
+ "description": "Vue SDK for the Fingprint service.",
5
+ "type": "module",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/types/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/types/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "dependencies": {
19
+ "@fingerru/agent": "1.0.0-next.0"
20
+ },
21
+ "devDependencies": {
22
+ "vue": "^3.0.0",
23
+ "@fingerru/protocol": "0.1.0",
24
+ "@fingerru/tsconfig": "0.1.0"
25
+ },
26
+ "peerDependencies": {
27
+ "vue": "^3.0.0"
28
+ },
29
+ "repository": "https://github.com/ifksco/fingerprint_agent",
30
+ "license": "MIT",
31
+ "sideEffects": false,
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "scripts": {
36
+ "build": "vite build",
37
+ "check:typecheck": "tsc --noEmit -p tsconfig.typecheck.json"
38
+ }
39
+ }