@shieldlabs-ai/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/CHANGELOG.md +58 -0
- package/LICENSE +21 -0
- package/README.md +589 -0
- package/dist/index.cjs +370 -0
- package/dist/index.d.cts +166 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.js +361 -0
- package/package.json +79 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { ShieldLabsError, IdentifyOptions, IdentifyResult, ShieldLabsAgent, LoadOptions } from '@shieldlabs-ai/js';
|
|
2
|
+
export { IdentifyOptions, IdentifyResult, InteractionIdentifier, LoadOptions, ShieldLabsAgent, ShieldLabsError, ShieldLabsErrorCode } from '@shieldlabs-ai/js';
|
|
3
|
+
import { Ref, App, MaybeRefOrGetter, InjectionKey } from 'vue';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* State of the agent for this app:
|
|
7
|
+
*
|
|
8
|
+
* - `loading`: not ready yet. Always the value during server-side rendering and in the first render
|
|
9
|
+
* of every component, so that hydration matches the server HTML. With `autoLoad: false` it stays
|
|
10
|
+
* `loading` until `load()` is called.
|
|
11
|
+
* - `ready`: the agent is loaded; identifications start right away.
|
|
12
|
+
* - `error`: the last load failed. `error` says why; the next `identify()`, `check()`, `getAgent()`
|
|
13
|
+
* or `load()` tries again.
|
|
14
|
+
*/
|
|
15
|
+
type ShieldLabsStatus = 'loading' | 'ready' | 'error';
|
|
16
|
+
/**
|
|
17
|
+
* `checkOnLoad` plugin option. `true` runs one anonymous `check()` when the agent is ready,
|
|
18
|
+
* `{ userId }` runs it for a User HID. Default `false`.
|
|
19
|
+
*/
|
|
20
|
+
type CheckOnLoadOption = boolean | {
|
|
21
|
+
userId?: string | null;
|
|
22
|
+
};
|
|
23
|
+
/** Options of `createShieldLabs()`: the `@shieldlabs-ai/js` load options plus the plugin options. */
|
|
24
|
+
interface ShieldLabsOptions extends LoadOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Runs `check()` once when the agent becomes ready, for passive monitoring of the visit. The agent
|
|
27
|
+
* limits it to one per visit every five minutes. Skipped when an `identify()` or `check()` for the
|
|
28
|
+
* same User HID is already in flight at that moment: that call identifies the visit, and the agent
|
|
29
|
+
* runs one identification at a time for a User HID. Default `false`.
|
|
30
|
+
*/
|
|
31
|
+
checkOnLoad?: CheckOnLoadOption;
|
|
32
|
+
/**
|
|
33
|
+
* Loads the agent right after the app is mounted (default `true`). With `false` nothing loads until
|
|
34
|
+
* `load()` from `useShieldLabs()` is called, for example once the visitor has given consent. Until
|
|
35
|
+
* then `status` stays `loading` and `getAgent()` waits, while `identify()` and `check()` fail fast
|
|
36
|
+
* instead of waiting: `identify()` rejects with `not_initialized`, `check()` resolves `null`, and
|
|
37
|
+
* the `identify()` of `useIdentify()` resolves `null` with a `not_initialized` error.
|
|
38
|
+
*/
|
|
39
|
+
autoLoad?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/** The Vue plugin returned by {@link createShieldLabs}. Pass it to `app.use()`. */
|
|
42
|
+
interface ShieldLabsPlugin {
|
|
43
|
+
install: (app: App) => void;
|
|
44
|
+
}
|
|
45
|
+
/** What `useShieldLabs()` returns, and what the plugin provides under `shieldLabsKey`. */
|
|
46
|
+
interface ShieldLabsContext {
|
|
47
|
+
/** `loading`, `ready` or `error`. */
|
|
48
|
+
readonly status: Readonly<Ref<ShieldLabsStatus>>;
|
|
49
|
+
/** Why the last load failed, otherwise `null`. */
|
|
50
|
+
readonly error: Readonly<Ref<ShieldLabsError | null>>;
|
|
51
|
+
/**
|
|
52
|
+
* Fresh identification with a new request ID. Waits for the agent; `timeout` covers the wait and
|
|
53
|
+
* the agent's answer together. Rejects with a `ShieldLabsError` when there is no identification,
|
|
54
|
+
* and with `not_initialized` right away while `autoLoad: false` waits for `load()`.
|
|
55
|
+
*/
|
|
56
|
+
identify: (options?: IdentifyOptions) => Promise<IdentifyResult>;
|
|
57
|
+
/**
|
|
58
|
+
* Background check, limited by the agent to one per visit every five minutes. Resolves `null`
|
|
59
|
+
* when the agent skipped it, and right away while `autoLoad: false` waits for `load()`. Waits for
|
|
60
|
+
* the agent like `identify()`.
|
|
61
|
+
*/
|
|
62
|
+
check: (options?: IdentifyOptions) => Promise<IdentifyResult | null>;
|
|
63
|
+
/**
|
|
64
|
+
* Starts loading the agent now, unless it is loaded or loading. With `autoLoad: false` nothing
|
|
65
|
+
* loads before this call. After a failed load it loads again. The outcome shows in `status` and
|
|
66
|
+
* `error`. Does nothing during server-side rendering.
|
|
67
|
+
*/
|
|
68
|
+
load: () => void;
|
|
69
|
+
/**
|
|
70
|
+
* The agent of `@shieldlabs-ai/js` once it has loaded, for example to identify on the first
|
|
71
|
+
* interaction with a form: `getAgent().then((agent) => agent.identifyOnInteraction(form))`.
|
|
72
|
+
* Loads the agent when needed; with `autoLoad: false` it waits until `load()` is called, with no
|
|
73
|
+
* timeout of its own. Rejects with the `ShieldLabsError` of a failed load, and with
|
|
74
|
+
* `unsupported_environment` during server-side rendering.
|
|
75
|
+
*/
|
|
76
|
+
getAgent: () => Promise<ShieldLabsAgent>;
|
|
77
|
+
}
|
|
78
|
+
/** Return value of `useShieldLabs()`. */
|
|
79
|
+
type UseShieldLabsReturn = ShieldLabsContext;
|
|
80
|
+
/** Options of `useIdentify()`. */
|
|
81
|
+
interface UseIdentifyOptions {
|
|
82
|
+
/**
|
|
83
|
+
* User HID computed on your server, read when `identify()` runs. A string, a ref or a getter;
|
|
84
|
+
* `undefined` or `null` for anonymous identifications.
|
|
85
|
+
*/
|
|
86
|
+
userId?: MaybeRefOrGetter<string | null | undefined>;
|
|
87
|
+
/**
|
|
88
|
+
* Calls `identify()` once when the component is mounted (after the agent is ready). Each call is
|
|
89
|
+
* a billable identification: use it sparingly. With `autoLoad: false` that identification ends
|
|
90
|
+
* right away with a `not_initialized` error unless `load()` was called before the component
|
|
91
|
+
* mounted, and a later `load()` does not start it again. Default `false`.
|
|
92
|
+
*/
|
|
93
|
+
runOnMount?: boolean;
|
|
94
|
+
}
|
|
95
|
+
/** Return value of `useIdentify()`. */
|
|
96
|
+
interface UseIdentifyReturn {
|
|
97
|
+
/**
|
|
98
|
+
* Runs a fresh identification. Resolves with the result, or `null` when there is no
|
|
99
|
+
* identification (the reason is in `error`); it never rejects. Options override the composable's
|
|
100
|
+
* `userId` for this call, and `timeout` covers the wait for the agent and its answer together.
|
|
101
|
+
* While a call of this helper with the same `userId` and `timeout` is in flight (a double submit),
|
|
102
|
+
* it returns that call instead of starting another identification. A call without `timeout`
|
|
103
|
+
* counts as one with the plugin's `timeout` (10 seconds by default).
|
|
104
|
+
*/
|
|
105
|
+
identify: (options?: IdentifyOptions) => Promise<IdentifyResult | null>;
|
|
106
|
+
/** The result of the latest `identify()`, `null` before it resolves or when it failed. */
|
|
107
|
+
readonly result: Readonly<Ref<IdentifyResult | null>>;
|
|
108
|
+
/** `true` while the latest `identify()` runs. */
|
|
109
|
+
readonly isLoading: Readonly<Ref<boolean>>;
|
|
110
|
+
/** Why the latest `identify()` returned `null`, otherwise `null`. */
|
|
111
|
+
readonly error: Readonly<Ref<ShieldLabsError | null>>;
|
|
112
|
+
/**
|
|
113
|
+
* Clears `result`, `error` and `isLoading`. A call still in flight no longer updates them, unless
|
|
114
|
+
* `identify()` is called again with the same `userId` and `timeout` while it runs: that call
|
|
115
|
+
* returns it.
|
|
116
|
+
*/
|
|
117
|
+
reset: () => void;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Creates the ShieldLabs plugin for `app.use()`. The plugin loads the agent once per app, after the
|
|
122
|
+
* app is mounted in the browser (with `autoLoad: false`, when `load()` from `useShieldLabs()` is
|
|
123
|
+
* called): never during server-side rendering and never before hydration.
|
|
124
|
+
* The load options (`publicKey`, `environment`, `scriptUrl`, `timeout`) are checked by
|
|
125
|
+
* `@shieldlabs-ai/js` when the agent loads; a problem shows up in `status` and `error`.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* createApp(App)
|
|
129
|
+
* .use(createShieldLabs({ publicKey: import.meta.env.VITE_SHIELDLABS_PUBLIC_KEY }))
|
|
130
|
+
* .mount('#app');
|
|
131
|
+
*/
|
|
132
|
+
declare function createShieldLabs(options: ShieldLabsOptions): ShieldLabsPlugin;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The agent state of the app (`status`, `error`), its `identify()` and `check()`, `load()` to start
|
|
136
|
+
* loading (with `autoLoad: false`, for example after consent) and `getAgent()` for the agent itself.
|
|
137
|
+
*
|
|
138
|
+
* Inside a component, `status` is `loading` and `error` is `null` until the component is mounted,
|
|
139
|
+
* as during server-side rendering, so hydration always matches the server HTML. Nothing loads or
|
|
140
|
+
* identifies during server-side rendering.
|
|
141
|
+
*/
|
|
142
|
+
declare function useShieldLabs(): UseShieldLabsReturn;
|
|
143
|
+
/**
|
|
144
|
+
* An identify helper with `result`, `isLoading` and `error` refs. `identify()` never rejects: it
|
|
145
|
+
* resolves `null` when there is no identification, so the protected action can go ahead as
|
|
146
|
+
* unverified. `userId` is read when `identify()` runs; changing it never starts an identification.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* const { identify, isLoading } = useIdentify({ userId: () => user.value?.hid });
|
|
150
|
+
* async function onSubmit() {
|
|
151
|
+
* const result = await identify();
|
|
152
|
+
* await fetch('/api/signup', { method: 'POST', body: JSON.stringify({ requestId: result?.requestId ?? null }) });
|
|
153
|
+
* }
|
|
154
|
+
*/
|
|
155
|
+
declare function useIdentify(options?: UseIdentifyOptions): UseIdentifyReturn;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Injection key under which the plugin provides the {@link ShieldLabsContext} of an app. Use it
|
|
159
|
+
* with `provide()` to give components a stand-in context in tests.
|
|
160
|
+
*/
|
|
161
|
+
declare const shieldLabsKey: InjectionKey<ShieldLabsContext>;
|
|
162
|
+
|
|
163
|
+
/** Version of `@shieldlabs-ai/vue`. A test keeps it equal to `package.json`. */
|
|
164
|
+
declare const VERSION = "1.0.0";
|
|
165
|
+
|
|
166
|
+
export { type CheckOnLoadOption, type ShieldLabsContext, type ShieldLabsOptions, type ShieldLabsPlugin, type ShieldLabsStatus, type UseIdentifyOptions, type UseIdentifyReturn, type UseShieldLabsReturn, VERSION, createShieldLabs, shieldLabsKey, useIdentify, useShieldLabs };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { ShieldLabsError, load } from '@shieldlabs-ai/js';
|
|
2
|
+
export { ShieldLabsError } from '@shieldlabs-ai/js';
|
|
3
|
+
import { getCurrentInstance, shallowRef, onMounted, computed, shallowReadonly, toValue, hasInjectionContext, inject } from 'vue';
|
|
4
|
+
|
|
5
|
+
// src/plugin.ts
|
|
6
|
+
var shieldLabsKey = /* @__PURE__ */ Symbol("shieldlabs");
|
|
7
|
+
var DEFAULT_TIMEOUT = 1e4;
|
|
8
|
+
var MAX_TIMEOUT = 2147483647;
|
|
9
|
+
var internals = /* @__PURE__ */ new WeakMap();
|
|
10
|
+
var serverContext;
|
|
11
|
+
function isBrowser() {
|
|
12
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
13
|
+
}
|
|
14
|
+
function toShieldLabsError(reason, code, message) {
|
|
15
|
+
return reason instanceof ShieldLabsError ? reason : new ShieldLabsError(code, message, reason);
|
|
16
|
+
}
|
|
17
|
+
function isObject(value) {
|
|
18
|
+
return typeof value === "object" && value !== null;
|
|
19
|
+
}
|
|
20
|
+
function isTimeout(value) {
|
|
21
|
+
return typeof value === "number" && value > 0 && value <= MAX_TIMEOUT;
|
|
22
|
+
}
|
|
23
|
+
function ignore() {
|
|
24
|
+
}
|
|
25
|
+
function userOf(options) {
|
|
26
|
+
return isObject(options) && typeof options.userId === "string" ? options.userId : void 0;
|
|
27
|
+
}
|
|
28
|
+
function withTimeLeft(options, left, total) {
|
|
29
|
+
const raw = options;
|
|
30
|
+
if (left >= total) return options;
|
|
31
|
+
if (raw === void 0 || raw === null) return { timeout: left };
|
|
32
|
+
if (!isObject(raw) || raw.timeout !== void 0 && !isTimeout(raw.timeout)) return options;
|
|
33
|
+
return { ...options, timeout: left };
|
|
34
|
+
}
|
|
35
|
+
function createContext(loadOptions, checkOnLoad, autoLoad) {
|
|
36
|
+
const status = shallowRef("loading");
|
|
37
|
+
const error = shallowRef(null);
|
|
38
|
+
let loading;
|
|
39
|
+
let agent;
|
|
40
|
+
let loadAllowed = autoLoad;
|
|
41
|
+
let started = false;
|
|
42
|
+
let checked = false;
|
|
43
|
+
const inFlight = [];
|
|
44
|
+
let allow = ignore;
|
|
45
|
+
const allowed = new Promise((resolve) => {
|
|
46
|
+
allow = resolve;
|
|
47
|
+
});
|
|
48
|
+
allowed.catch(ignore);
|
|
49
|
+
const defaultTimeout = isTimeout(loadOptions.timeout) ? loadOptions.timeout : DEFAULT_TIMEOUT;
|
|
50
|
+
const runCheckOnLoad = (ready) => {
|
|
51
|
+
var _a;
|
|
52
|
+
if (checkOnLoad === false || checked) return;
|
|
53
|
+
checked = true;
|
|
54
|
+
const userId = checkOnLoad === true ? void 0 : (_a = checkOnLoad.userId) != null ? _a : void 0;
|
|
55
|
+
if (inFlight.includes(userId)) return;
|
|
56
|
+
const options = userId === void 0 ? void 0 : { userId };
|
|
57
|
+
new Promise((resolve) => {
|
|
58
|
+
resolve(ready.check(options));
|
|
59
|
+
}).catch((reason) => {
|
|
60
|
+
if (reason instanceof ShieldLabsError && reason.code === "invalid_options") {
|
|
61
|
+
console.warn("[ShieldLabs] checkOnLoad: " + reason.message);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
const startLoad = () => {
|
|
66
|
+
if (loading) return loading;
|
|
67
|
+
started = true;
|
|
68
|
+
const next = new Promise((resolve) => {
|
|
69
|
+
resolve(load(loadOptions));
|
|
70
|
+
}).catch((reason) => {
|
|
71
|
+
throw toShieldLabsError(reason, "load_failed", "Could not load the ShieldLabs agent.");
|
|
72
|
+
});
|
|
73
|
+
loading = next;
|
|
74
|
+
status.value = "loading";
|
|
75
|
+
error.value = null;
|
|
76
|
+
next.then(
|
|
77
|
+
(ready) => {
|
|
78
|
+
agent = ready;
|
|
79
|
+
status.value = "ready";
|
|
80
|
+
runCheckOnLoad(ready);
|
|
81
|
+
},
|
|
82
|
+
(reason) => {
|
|
83
|
+
loading = void 0;
|
|
84
|
+
const failure = toShieldLabsError(reason, "load_failed", "Could not load the ShieldLabs agent.");
|
|
85
|
+
if (failure.code === "invalid_options") console.warn("[ShieldLabs] " + failure.message);
|
|
86
|
+
error.value = failure;
|
|
87
|
+
status.value = "error";
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
return next;
|
|
91
|
+
};
|
|
92
|
+
const getAgent = () => {
|
|
93
|
+
if (!isBrowser()) return load(loadOptions);
|
|
94
|
+
return loadAllowed ? startLoad() : allowed;
|
|
95
|
+
};
|
|
96
|
+
const loadNow = () => {
|
|
97
|
+
if (!isBrowser()) return;
|
|
98
|
+
const first = !loadAllowed;
|
|
99
|
+
loadAllowed = true;
|
|
100
|
+
const next = startLoad();
|
|
101
|
+
if (first) allow(next);
|
|
102
|
+
};
|
|
103
|
+
const beforeLoad = () => !loadAllowed && isBrowser();
|
|
104
|
+
const notLoaded = () => new ShieldLabsError("not_initialized", "The ShieldLabs agent is not loaded. With autoLoad: false it loads once load() is called.");
|
|
105
|
+
const timedOut = (total) => new ShieldLabsError("timeout", "The ShieldLabs agent did not load within " + String(total) + " ms.");
|
|
106
|
+
const withAgent = (options, call) => {
|
|
107
|
+
const user = userOf(options);
|
|
108
|
+
inFlight.push(user);
|
|
109
|
+
const outcome = new Promise((resolve, reject) => {
|
|
110
|
+
if (agent) {
|
|
111
|
+
resolve(call(agent, options));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const raw = options;
|
|
115
|
+
const own = isObject(raw) ? raw.timeout : void 0;
|
|
116
|
+
const total = isTimeout(own) ? own : defaultTimeout;
|
|
117
|
+
const startedAt = Date.now();
|
|
118
|
+
let settled = false;
|
|
119
|
+
const timer = setTimeout(() => {
|
|
120
|
+
settled = true;
|
|
121
|
+
reject(timedOut(total));
|
|
122
|
+
}, total);
|
|
123
|
+
getAgent().then(
|
|
124
|
+
(ready) => {
|
|
125
|
+
if (settled) return;
|
|
126
|
+
settled = true;
|
|
127
|
+
clearTimeout(timer);
|
|
128
|
+
const left = total - (Date.now() - startedAt);
|
|
129
|
+
if (left < 1) reject(timedOut(total));
|
|
130
|
+
else resolve(call(ready, withTimeLeft(options, left, total)));
|
|
131
|
+
},
|
|
132
|
+
(reason) => {
|
|
133
|
+
if (settled) return;
|
|
134
|
+
settled = true;
|
|
135
|
+
clearTimeout(timer);
|
|
136
|
+
reject(toShieldLabsError(reason, "load_failed", "Could not load the ShieldLabs agent."));
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
const finish = () => {
|
|
141
|
+
inFlight.splice(inFlight.indexOf(user), 1);
|
|
142
|
+
};
|
|
143
|
+
outcome.then(finish, finish);
|
|
144
|
+
return outcome;
|
|
145
|
+
};
|
|
146
|
+
const context = {
|
|
147
|
+
status: shallowReadonly(status),
|
|
148
|
+
error: shallowReadonly(error),
|
|
149
|
+
// Like the agent: identify() rejects with not_initialized, check() resolves null.
|
|
150
|
+
identify: (options) => beforeLoad() ? Promise.reject(notLoaded()) : withAgent(options, (ready, agentOptions) => ready.identify(agentOptions)),
|
|
151
|
+
check: (options) => beforeLoad() ? Promise.resolve(null) : withAgent(options, (ready, agentOptions) => ready.check(agentOptions)),
|
|
152
|
+
load: loadNow,
|
|
153
|
+
getAgent
|
|
154
|
+
};
|
|
155
|
+
internals.set(context, {
|
|
156
|
+
// With autoLoad the plugin starts loading once the app is mounted, unless a load has already
|
|
157
|
+
// started. After a failed load only identify(), check(), getAgent() and load() load again.
|
|
158
|
+
start: () => {
|
|
159
|
+
if (started || !autoLoad || !isBrowser()) return;
|
|
160
|
+
void startLoad();
|
|
161
|
+
},
|
|
162
|
+
defaultTimeout
|
|
163
|
+
});
|
|
164
|
+
return context;
|
|
165
|
+
}
|
|
166
|
+
function startContext(context) {
|
|
167
|
+
var _a;
|
|
168
|
+
(_a = internals.get(context)) == null ? void 0 : _a.start();
|
|
169
|
+
}
|
|
170
|
+
function defaultTimeoutOf(context) {
|
|
171
|
+
var _a, _b;
|
|
172
|
+
return (_b = (_a = internals.get(context)) == null ? void 0 : _a.defaultTimeout) != null ? _b : DEFAULT_TIMEOUT;
|
|
173
|
+
}
|
|
174
|
+
function createServerContext() {
|
|
175
|
+
const unsupported = () => Promise.reject(
|
|
176
|
+
new ShieldLabsError("unsupported_environment", "ShieldLabs identifies in the browser, not during server-side rendering.")
|
|
177
|
+
);
|
|
178
|
+
return {
|
|
179
|
+
status: shallowReadonly(shallowRef("loading")),
|
|
180
|
+
error: shallowReadonly(shallowRef(null)),
|
|
181
|
+
identify: unsupported,
|
|
182
|
+
check: unsupported,
|
|
183
|
+
load: ignore,
|
|
184
|
+
getAgent: unsupported
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function injectContext(caller) {
|
|
188
|
+
const inContext = hasInjectionContext();
|
|
189
|
+
const context = inContext ? inject(shieldLabsKey, null) : null;
|
|
190
|
+
if (context) return context;
|
|
191
|
+
if (!isBrowser()) return serverContext != null ? serverContext : serverContext = createServerContext();
|
|
192
|
+
if (!inContext) {
|
|
193
|
+
throw new Error("[ShieldLabs] " + caller + "() must be called inside setup() of a component or in app.runWithContext().");
|
|
194
|
+
}
|
|
195
|
+
throw new Error(
|
|
196
|
+
"[ShieldLabs] " + caller + "() found no ShieldLabs plugin. Install it before mounting the app: app.use(createShieldLabs({ publicKey })). In Nuxt, add the client plugin plugins/shieldlabs.client.ts."
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/plugin.ts
|
|
201
|
+
var installedApps = /* @__PURE__ */ new WeakSet();
|
|
202
|
+
function invalidOptions(message) {
|
|
203
|
+
return new ShieldLabsError("invalid_options", message);
|
|
204
|
+
}
|
|
205
|
+
function checkCheckOnLoad(value) {
|
|
206
|
+
if (value === void 0 || value === false) return false;
|
|
207
|
+
if (value === true) return true;
|
|
208
|
+
if (typeof value === "object" && value !== null) {
|
|
209
|
+
const userId = value.userId;
|
|
210
|
+
if (userId === void 0 || userId === null) return {};
|
|
211
|
+
if (typeof userId === "string") return { userId };
|
|
212
|
+
}
|
|
213
|
+
throw invalidOptions("checkOnLoad must be true, false or { userId }.");
|
|
214
|
+
}
|
|
215
|
+
function createShieldLabs(options) {
|
|
216
|
+
var _a;
|
|
217
|
+
const candidate = options;
|
|
218
|
+
if (typeof candidate !== "object" || candidate === null) {
|
|
219
|
+
throw invalidOptions("createShieldLabs() needs an options object with the Public Key of your domain: createShieldLabs({ publicKey }).");
|
|
220
|
+
}
|
|
221
|
+
const checkOnLoad = checkCheckOnLoad(options.checkOnLoad);
|
|
222
|
+
const autoLoad = (_a = options.autoLoad) != null ? _a : true;
|
|
223
|
+
if (typeof autoLoad !== "boolean") throw invalidOptions("autoLoad must be true or false.");
|
|
224
|
+
const loadOptions = { publicKey: options.publicKey };
|
|
225
|
+
if (options.environment !== void 0) loadOptions.environment = options.environment;
|
|
226
|
+
if (options.scriptUrl !== void 0) loadOptions.scriptUrl = options.scriptUrl;
|
|
227
|
+
if (options.timeout !== void 0) loadOptions.timeout = options.timeout;
|
|
228
|
+
return {
|
|
229
|
+
install(app) {
|
|
230
|
+
if (installedApps.has(app)) {
|
|
231
|
+
console.warn("[ShieldLabs] This app already has a ShieldLabs plugin; the second one is ignored.");
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
installedApps.add(app);
|
|
235
|
+
const context = createContext(loadOptions, checkOnLoad, autoLoad);
|
|
236
|
+
app.provide(shieldLabsKey, context);
|
|
237
|
+
const mount = app.mount.bind(app);
|
|
238
|
+
app.mount = (...args) => {
|
|
239
|
+
const root = mount(...args);
|
|
240
|
+
startContext(context);
|
|
241
|
+
return root;
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function useShieldLabs() {
|
|
247
|
+
const context = injectContext("useShieldLabs");
|
|
248
|
+
if (!getCurrentInstance()) return context;
|
|
249
|
+
const mounted = shallowRef(false);
|
|
250
|
+
onMounted(() => {
|
|
251
|
+
mounted.value = true;
|
|
252
|
+
startContext(context);
|
|
253
|
+
});
|
|
254
|
+
return {
|
|
255
|
+
status: computed(() => mounted.value ? context.status.value : "loading"),
|
|
256
|
+
error: computed(() => mounted.value ? context.error.value : null),
|
|
257
|
+
identify: context.identify,
|
|
258
|
+
check: context.check,
|
|
259
|
+
load: context.load,
|
|
260
|
+
getAgent: context.getAgent
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function orUndefined(value) {
|
|
264
|
+
return value != null ? value : void 0;
|
|
265
|
+
}
|
|
266
|
+
function boundOf(timeout, fallback) {
|
|
267
|
+
return timeout === void 0 ? fallback : timeout;
|
|
268
|
+
}
|
|
269
|
+
function useIdentify(options = {}) {
|
|
270
|
+
const context = injectContext("useIdentify");
|
|
271
|
+
const defaultTimeout = defaultTimeoutOf(context);
|
|
272
|
+
const result = shallowRef(null);
|
|
273
|
+
const isLoading = shallowRef(false);
|
|
274
|
+
const error = shallowRef(null);
|
|
275
|
+
const running = /* @__PURE__ */ new Set();
|
|
276
|
+
let lastId = 0;
|
|
277
|
+
let owner = 0;
|
|
278
|
+
const own = (id) => {
|
|
279
|
+
owner = id;
|
|
280
|
+
result.value = null;
|
|
281
|
+
error.value = null;
|
|
282
|
+
isLoading.value = true;
|
|
283
|
+
};
|
|
284
|
+
const identify = (overrides) => {
|
|
285
|
+
const given = overrides != null ? overrides : {};
|
|
286
|
+
let callOptions = given;
|
|
287
|
+
let key;
|
|
288
|
+
if (typeof given === "object" && given !== null) {
|
|
289
|
+
const picked = given;
|
|
290
|
+
const userId = "userId" in picked ? orUndefined(picked.userId) : orUndefined(toValue(options.userId));
|
|
291
|
+
const timeout = picked.timeout;
|
|
292
|
+
const bound = boundOf(timeout, defaultTimeout);
|
|
293
|
+
for (const call of running) {
|
|
294
|
+
if (call.userId === userId && Object.is(call.timeout, bound)) {
|
|
295
|
+
if (owner !== call.id) own(call.id);
|
|
296
|
+
return call.promise;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
key = { userId, timeout: bound };
|
|
300
|
+
const built = {};
|
|
301
|
+
if (userId !== void 0) built.userId = userId;
|
|
302
|
+
if (timeout !== void 0) built.timeout = timeout;
|
|
303
|
+
callOptions = built;
|
|
304
|
+
}
|
|
305
|
+
const id = ++lastId;
|
|
306
|
+
own(id);
|
|
307
|
+
const promise = new Promise((resolve) => {
|
|
308
|
+
resolve(context.identify(callOptions));
|
|
309
|
+
}).then(
|
|
310
|
+
(value) => {
|
|
311
|
+
if (owner === id) {
|
|
312
|
+
result.value = value;
|
|
313
|
+
isLoading.value = false;
|
|
314
|
+
}
|
|
315
|
+
return value;
|
|
316
|
+
},
|
|
317
|
+
(reason) => {
|
|
318
|
+
const failure = toShieldLabsError(reason, "not_initialized", "The identification did not start.");
|
|
319
|
+
if (owner === id) {
|
|
320
|
+
error.value = failure;
|
|
321
|
+
isLoading.value = false;
|
|
322
|
+
}
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
if (key !== void 0) {
|
|
327
|
+
const entry = { id, ...key, promise };
|
|
328
|
+
running.add(entry);
|
|
329
|
+
void promise.then(() => {
|
|
330
|
+
running.delete(entry);
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
return promise;
|
|
334
|
+
};
|
|
335
|
+
const reset = () => {
|
|
336
|
+
owner = 0;
|
|
337
|
+
result.value = null;
|
|
338
|
+
error.value = null;
|
|
339
|
+
isLoading.value = false;
|
|
340
|
+
};
|
|
341
|
+
if (getCurrentInstance()) {
|
|
342
|
+
onMounted(() => {
|
|
343
|
+
startContext(context);
|
|
344
|
+
if (options.runOnMount === true) void identify();
|
|
345
|
+
});
|
|
346
|
+
} else if (options.runOnMount === true && isBrowser()) {
|
|
347
|
+
void identify();
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
identify,
|
|
351
|
+
result: shallowReadonly(result),
|
|
352
|
+
isLoading: shallowReadonly(isLoading),
|
|
353
|
+
error: shallowReadonly(error),
|
|
354
|
+
reset
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/version.ts
|
|
359
|
+
var VERSION = "1.0.0";
|
|
360
|
+
|
|
361
|
+
export { VERSION, createShieldLabs, shieldLabsKey, useIdentify, useShieldLabs };
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shieldlabs-ai/vue",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Vue 3 and Nuxt bindings for ShieldLabs device intelligence: a plugin that loads the agent once and composables that return a request ID with loading and error state.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"shieldlabs",
|
|
7
|
+
"vue",
|
|
8
|
+
"nuxt",
|
|
9
|
+
"device-intelligence",
|
|
10
|
+
"fraud-detection",
|
|
11
|
+
"risk-score",
|
|
12
|
+
"identification"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://docs.shieldlabs.ai",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/ShieldLabs-ai/shieldlabs-vue/issues",
|
|
17
|
+
"email": "contact@shieldlabs.ai"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/ShieldLabs-ai/shieldlabs-vue.git"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "ShieldLabs Inc. <contact@shieldlabs.ai>",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"main": "./dist/index.cjs",
|
|
28
|
+
"module": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"import": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"require": {
|
|
37
|
+
"types": "./dist/index.d.cts",
|
|
38
|
+
"default": "./dist/index.cjs"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"./package.json": "./package.json"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"CHANGELOG.md"
|
|
46
|
+
],
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18"
|
|
49
|
+
},
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup",
|
|
55
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.build.json",
|
|
56
|
+
"lint": "eslint .",
|
|
57
|
+
"pretest": "npm run build",
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"prepublishOnly": "npm run build"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"@shieldlabs-ai/js": "^1.0.0",
|
|
63
|
+
"vue": "^3.3.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@eslint/js": "~9.39.5",
|
|
67
|
+
"@types/node": "^22.0.0",
|
|
68
|
+
"@vitest/coverage-v8": "~3.2.7",
|
|
69
|
+
"@vue/test-utils": "^2.4.6",
|
|
70
|
+
"eslint": "~9.39.5",
|
|
71
|
+
"happy-dom": "^20.14.5",
|
|
72
|
+
"tsup": "^8.5.1",
|
|
73
|
+
"typescript": "~5.9.3",
|
|
74
|
+
"typescript-eslint": "^8.71.0",
|
|
75
|
+
"vite": "~6.4.3",
|
|
76
|
+
"vitest": "~3.2.7",
|
|
77
|
+
"vue": "~3.5.22"
|
|
78
|
+
}
|
|
79
|
+
}
|