@rajeev02/vault 0.1.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/lib/hooks.d.ts +103 -0
- package/lib/hooks.d.ts.map +1 -0
- package/lib/hooks.js +158 -0
- package/lib/hooks.js.map +1 -0
- package/lib/index.d.ts +36 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +49 -0
- package/lib/index.js.map +1 -0
- package/lib/native-bridge.d.ts +8 -0
- package/lib/native-bridge.d.ts.map +1 -0
- package/lib/native-bridge.js +30 -0
- package/lib/native-bridge.js.map +1 -0
- package/lib/types.d.ts +96 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +32 -0
- package/lib/types.js.map +1 -0
- package/lib/vault.d.ts +239 -0
- package/lib/vault.d.ts.map +1 -0
- package/lib/vault.js +342 -0
- package/lib/vault.js.map +1 -0
- package/package.json +60 -0
package/lib/hooks.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Vault } from './vault';
|
|
2
|
+
import type { VaultConfig, VaultStats } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* React hook to create and manage a Vault instance.
|
|
5
|
+
*
|
|
6
|
+
* @param config - Vault configuration
|
|
7
|
+
* @returns Vault instance and loading state
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* function App() {
|
|
12
|
+
* const { vault, isReady, error } = useVault({ appId: 'com.myapp' });
|
|
13
|
+
*
|
|
14
|
+
* if (!isReady) return <LoadingScreen />;
|
|
15
|
+
* if (error) return <ErrorScreen error={error} />;
|
|
16
|
+
*
|
|
17
|
+
* return <MainApp vault={vault!} />;
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function useVault(config: VaultConfig): {
|
|
22
|
+
vault: Vault | null;
|
|
23
|
+
isReady: boolean;
|
|
24
|
+
error: Error | null;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* React hook to read a value from the vault reactively.
|
|
28
|
+
* Re-reads when the key or namespace changes.
|
|
29
|
+
*
|
|
30
|
+
* @param vault - Vault instance
|
|
31
|
+
* @param key - Key to read
|
|
32
|
+
* @param namespace - Optional namespace
|
|
33
|
+
* @returns Value, loading state, and refresh function
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* function TokenDisplay({ vault }: { vault: Vault }) {
|
|
38
|
+
* const { value, isLoading, refresh } = useVaultValue(vault, 'auth_token');
|
|
39
|
+
*
|
|
40
|
+
* if (isLoading) return <Text>Loading...</Text>;
|
|
41
|
+
*
|
|
42
|
+
* return (
|
|
43
|
+
* <View>
|
|
44
|
+
* <Text>Token: {value ?? 'Not set'}</Text>
|
|
45
|
+
* <Button title="Refresh" onPress={refresh} />
|
|
46
|
+
* </View>
|
|
47
|
+
* );
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function useVaultValue(vault: Vault | null, key: string, namespace?: string): {
|
|
52
|
+
value: string | null;
|
|
53
|
+
isLoading: boolean;
|
|
54
|
+
error: Error | null;
|
|
55
|
+
refresh: () => Promise<void>;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* React hook to read a JSON value from the vault.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```tsx
|
|
62
|
+
* interface UserProfile {
|
|
63
|
+
* name: string;
|
|
64
|
+
* email: string;
|
|
65
|
+
* }
|
|
66
|
+
*
|
|
67
|
+
* function Profile({ vault }: { vault: Vault }) {
|
|
68
|
+
* const { data, isLoading } = useVaultJSON<UserProfile>(vault, 'profile');
|
|
69
|
+
*
|
|
70
|
+
* if (isLoading) return <Text>Loading...</Text>;
|
|
71
|
+
* return <Text>Hello, {data?.name ?? 'Guest'}</Text>;
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare function useVaultJSON<T = unknown>(vault: Vault | null, key: string, namespace?: string): {
|
|
76
|
+
data: T | null;
|
|
77
|
+
isLoading: boolean;
|
|
78
|
+
error: Error | null;
|
|
79
|
+
refresh: () => Promise<void>;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* React hook to get vault statistics.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```tsx
|
|
86
|
+
* function VaultInfo({ vault }: { vault: Vault }) {
|
|
87
|
+
* const { stats, isLoading } = useVaultStats(vault);
|
|
88
|
+
*
|
|
89
|
+
* return (
|
|
90
|
+
* <Text>
|
|
91
|
+
* Entries: {stats?.totalEntries ?? 0}
|
|
92
|
+
* Size: {stats?.storageBytes ?? 0} bytes
|
|
93
|
+
* </Text>
|
|
94
|
+
* );
|
|
95
|
+
* }
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export declare function useVaultStats(vault: Vault | null): {
|
|
99
|
+
stats: VaultStats | null;
|
|
100
|
+
isLoading: boolean;
|
|
101
|
+
refresh: () => Promise<void>;
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,KAAK,EAAE,WAAW,EAAgB,UAAU,EAAE,MAAM,SAAS,CAAC;AAErE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,WAAW;;;;EAsB3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM;;;;;EAyBnB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,EACtC,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM;;;;;EAOnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;;;;EAoBhD"}
|
package/lib/hooks.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useVault = useVault;
|
|
4
|
+
exports.useVaultValue = useVaultValue;
|
|
5
|
+
exports.useVaultJSON = useVaultJSON;
|
|
6
|
+
exports.useVaultStats = useVaultStats;
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const vault_1 = require("./vault");
|
|
9
|
+
/**
|
|
10
|
+
* React hook to create and manage a Vault instance.
|
|
11
|
+
*
|
|
12
|
+
* @param config - Vault configuration
|
|
13
|
+
* @returns Vault instance and loading state
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* function App() {
|
|
18
|
+
* const { vault, isReady, error } = useVault({ appId: 'com.myapp' });
|
|
19
|
+
*
|
|
20
|
+
* if (!isReady) return <LoadingScreen />;
|
|
21
|
+
* if (error) return <ErrorScreen error={error} />;
|
|
22
|
+
*
|
|
23
|
+
* return <MainApp vault={vault!} />;
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
function useVault(config) {
|
|
28
|
+
const [vault, setVault] = (0, react_1.useState)(null);
|
|
29
|
+
const [isReady, setIsReady] = (0, react_1.useState)(false);
|
|
30
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
31
|
+
const initRef = (0, react_1.useRef)(false);
|
|
32
|
+
(0, react_1.useEffect)(() => {
|
|
33
|
+
if (initRef.current)
|
|
34
|
+
return;
|
|
35
|
+
initRef.current = true;
|
|
36
|
+
vault_1.Vault.create(config)
|
|
37
|
+
.then((v) => {
|
|
38
|
+
setVault(v);
|
|
39
|
+
setIsReady(true);
|
|
40
|
+
})
|
|
41
|
+
.catch((e) => {
|
|
42
|
+
setError(e);
|
|
43
|
+
setIsReady(true);
|
|
44
|
+
});
|
|
45
|
+
}, [config.appId]);
|
|
46
|
+
return { vault, isReady, error };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* React hook to read a value from the vault reactively.
|
|
50
|
+
* Re-reads when the key or namespace changes.
|
|
51
|
+
*
|
|
52
|
+
* @param vault - Vault instance
|
|
53
|
+
* @param key - Key to read
|
|
54
|
+
* @param namespace - Optional namespace
|
|
55
|
+
* @returns Value, loading state, and refresh function
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```tsx
|
|
59
|
+
* function TokenDisplay({ vault }: { vault: Vault }) {
|
|
60
|
+
* const { value, isLoading, refresh } = useVaultValue(vault, 'auth_token');
|
|
61
|
+
*
|
|
62
|
+
* if (isLoading) return <Text>Loading...</Text>;
|
|
63
|
+
*
|
|
64
|
+
* return (
|
|
65
|
+
* <View>
|
|
66
|
+
* <Text>Token: {value ?? 'Not set'}</Text>
|
|
67
|
+
* <Button title="Refresh" onPress={refresh} />
|
|
68
|
+
* </View>
|
|
69
|
+
* );
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
function useVaultValue(vault, key, namespace) {
|
|
74
|
+
const [value, setValue] = (0, react_1.useState)(null);
|
|
75
|
+
const [isLoading, setIsLoading] = (0, react_1.useState)(true);
|
|
76
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
77
|
+
const refresh = (0, react_1.useCallback)(async () => {
|
|
78
|
+
if (!vault)
|
|
79
|
+
return;
|
|
80
|
+
setIsLoading(true);
|
|
81
|
+
try {
|
|
82
|
+
const result = await vault.get(key, namespace);
|
|
83
|
+
setValue(result);
|
|
84
|
+
setError(null);
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
setError(e);
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
setIsLoading(false);
|
|
91
|
+
}
|
|
92
|
+
}, [vault, key, namespace]);
|
|
93
|
+
(0, react_1.useEffect)(() => {
|
|
94
|
+
refresh();
|
|
95
|
+
}, [refresh]);
|
|
96
|
+
return { value, isLoading, error, refresh };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* React hook to read a JSON value from the vault.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```tsx
|
|
103
|
+
* interface UserProfile {
|
|
104
|
+
* name: string;
|
|
105
|
+
* email: string;
|
|
106
|
+
* }
|
|
107
|
+
*
|
|
108
|
+
* function Profile({ vault }: { vault: Vault }) {
|
|
109
|
+
* const { data, isLoading } = useVaultJSON<UserProfile>(vault, 'profile');
|
|
110
|
+
*
|
|
111
|
+
* if (isLoading) return <Text>Loading...</Text>;
|
|
112
|
+
* return <Text>Hello, {data?.name ?? 'Guest'}</Text>;
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
function useVaultJSON(vault, key, namespace) {
|
|
117
|
+
const { value, isLoading, error, refresh } = useVaultValue(vault, key, namespace);
|
|
118
|
+
const data = value ? JSON.parse(value) : null;
|
|
119
|
+
return { data, isLoading, error, refresh };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* React hook to get vault statistics.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```tsx
|
|
126
|
+
* function VaultInfo({ vault }: { vault: Vault }) {
|
|
127
|
+
* const { stats, isLoading } = useVaultStats(vault);
|
|
128
|
+
*
|
|
129
|
+
* return (
|
|
130
|
+
* <Text>
|
|
131
|
+
* Entries: {stats?.totalEntries ?? 0}
|
|
132
|
+
* Size: {stats?.storageBytes ?? 0} bytes
|
|
133
|
+
* </Text>
|
|
134
|
+
* );
|
|
135
|
+
* }
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
function useVaultStats(vault) {
|
|
139
|
+
const [stats, setStats] = (0, react_1.useState)(null);
|
|
140
|
+
const [isLoading, setIsLoading] = (0, react_1.useState)(true);
|
|
141
|
+
const refresh = (0, react_1.useCallback)(async () => {
|
|
142
|
+
if (!vault)
|
|
143
|
+
return;
|
|
144
|
+
setIsLoading(true);
|
|
145
|
+
try {
|
|
146
|
+
const s = await vault.stats();
|
|
147
|
+
setStats(s);
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
setIsLoading(false);
|
|
151
|
+
}
|
|
152
|
+
}, [vault]);
|
|
153
|
+
(0, react_1.useEffect)(() => {
|
|
154
|
+
refresh();
|
|
155
|
+
}, [refresh]);
|
|
156
|
+
return { stats, isLoading, refresh };
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=hooks.js.map
|
package/lib/hooks.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":";;AAsBA,4BAsBC;AA2BD,sCA4BC;AAoBD,oCAUC;AAmBD,sCAoBC;AAxKD,iCAAiE;AACjE,mCAAgC;AAGhC;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,QAAQ,CAAC,MAAmB;IAC1C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAA,gBAAQ,EAAe,IAAI,CAAC,CAAC;IACvD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,IAAA,gBAAQ,EAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAA,gBAAQ,EAAe,IAAI,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,IAAA,cAAM,EAAC,KAAK,CAAC,CAAC;IAE9B,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,OAAO,CAAC,OAAO;YAAE,OAAO;QAC5B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QAEvB,aAAK,CAAC,MAAM,CAAC,MAAM,CAAC;aACjB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YACV,QAAQ,CAAC,CAAC,CAAC,CAAC;YACZ,UAAU,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;YACX,QAAQ,CAAC,CAAC,CAAC,CAAC;YACZ,UAAU,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;IACP,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAEnB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AACnC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,SAAgB,aAAa,CAC3B,KAAmB,EACnB,GAAW,EACX,SAAkB;IAElB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAA,gBAAQ,EAAgB,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,IAAA,gBAAQ,EAAC,IAAI,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAA,gBAAQ,EAAe,IAAI,CAAC,CAAC;IAEvD,MAAM,OAAO,GAAG,IAAA,mBAAW,EAAC,KAAK,IAAI,EAAE;QACrC,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,YAAY,CAAC,IAAI,CAAC,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAC/C,QAAQ,CAAC,MAAM,CAAC,CAAC;YACjB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,QAAQ,CAAC,CAAU,CAAC,CAAC;QACvB,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC;IAE5B,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,OAAO,EAAE,CAAC;IACZ,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,YAAY,CAC1B,KAAmB,EACnB,GAAW,EACX,SAAkB;IAElB,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;IAElF,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAErD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAC7C,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,aAAa,CAAC,KAAmB;IAC/C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAA,gBAAQ,EAAoB,IAAI,CAAC,CAAC;IAC5D,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,IAAA,gBAAQ,EAAC,IAAI,CAAC,CAAC;IAEjD,MAAM,OAAO,GAAG,IAAA,mBAAW,EAAC,KAAK,IAAI,EAAE;QACrC,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,YAAY,CAAC,IAAI,CAAC,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;YAC9B,QAAQ,CAAC,CAAC,CAAC,CAAC;QACd,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,OAAO,EAAE,CAAC;IACZ,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AACvC,CAAC"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/vault
|
|
3
|
+
*
|
|
4
|
+
* Universal Secure Storage for React Native
|
|
5
|
+
* AES-256-GCM encrypted, cross-platform (Android, iOS, Web, Watch, Auto, IoT)
|
|
6
|
+
*
|
|
7
|
+
* Built with Rust core for maximum security and performance.
|
|
8
|
+
*
|
|
9
|
+
* @author Rajeev Kumar Joshi (https://github.com/Rajeev02)
|
|
10
|
+
* @license MIT
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { Vault } from '@rajeev02/vault';
|
|
15
|
+
*
|
|
16
|
+
* const vault = await Vault.create({ appId: 'com.myapp' });
|
|
17
|
+
*
|
|
18
|
+
* // Store
|
|
19
|
+
* await vault.set('token', 'my-secret', { expiry: '24h' });
|
|
20
|
+
*
|
|
21
|
+
* // Retrieve
|
|
22
|
+
* const token = await vault.get('token');
|
|
23
|
+
*
|
|
24
|
+
* // Namespaces
|
|
25
|
+
* const payments = vault.namespace('payments');
|
|
26
|
+
* await payments.set('upi_pin', '1234', { biometricRequired: true });
|
|
27
|
+
*
|
|
28
|
+
* // JSON helpers
|
|
29
|
+
* await vault.setJSON('user', { name: 'Rajeev', role: 'admin' });
|
|
30
|
+
* const user = await vault.getJSON<User>('user');
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export { Vault } from './vault';
|
|
34
|
+
export { VaultConfig, StoreOptions, VaultStats, VaultError, VaultErrorCode, } from './types';
|
|
35
|
+
export { useVault, useVaultValue, useVaultJSON, useVaultStats, } from './hooks';
|
|
36
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGhC,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,UAAU,EACV,cAAc,GACf,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,SAAS,CAAC"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @rajeev02/vault
|
|
4
|
+
*
|
|
5
|
+
* Universal Secure Storage for React Native
|
|
6
|
+
* AES-256-GCM encrypted, cross-platform (Android, iOS, Web, Watch, Auto, IoT)
|
|
7
|
+
*
|
|
8
|
+
* Built with Rust core for maximum security and performance.
|
|
9
|
+
*
|
|
10
|
+
* @author Rajeev Kumar Joshi (https://github.com/Rajeev02)
|
|
11
|
+
* @license MIT
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { Vault } from '@rajeev02/vault';
|
|
16
|
+
*
|
|
17
|
+
* const vault = await Vault.create({ appId: 'com.myapp' });
|
|
18
|
+
*
|
|
19
|
+
* // Store
|
|
20
|
+
* await vault.set('token', 'my-secret', { expiry: '24h' });
|
|
21
|
+
*
|
|
22
|
+
* // Retrieve
|
|
23
|
+
* const token = await vault.get('token');
|
|
24
|
+
*
|
|
25
|
+
* // Namespaces
|
|
26
|
+
* const payments = vault.namespace('payments');
|
|
27
|
+
* await payments.set('upi_pin', '1234', { biometricRequired: true });
|
|
28
|
+
*
|
|
29
|
+
* // JSON helpers
|
|
30
|
+
* await vault.setJSON('user', { name: 'Rajeev', role: 'admin' });
|
|
31
|
+
* const user = await vault.getJSON<User>('user');
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.useVaultStats = exports.useVaultJSON = exports.useVaultValue = exports.useVault = exports.VaultErrorCode = exports.VaultError = exports.Vault = void 0;
|
|
36
|
+
// Core
|
|
37
|
+
var vault_1 = require("./vault");
|
|
38
|
+
Object.defineProperty(exports, "Vault", { enumerable: true, get: function () { return vault_1.Vault; } });
|
|
39
|
+
// Types
|
|
40
|
+
var types_1 = require("./types");
|
|
41
|
+
Object.defineProperty(exports, "VaultError", { enumerable: true, get: function () { return types_1.VaultError; } });
|
|
42
|
+
Object.defineProperty(exports, "VaultErrorCode", { enumerable: true, get: function () { return types_1.VaultErrorCode; } });
|
|
43
|
+
// React Hooks
|
|
44
|
+
var hooks_1 = require("./hooks");
|
|
45
|
+
Object.defineProperty(exports, "useVault", { enumerable: true, get: function () { return hooks_1.useVault; } });
|
|
46
|
+
Object.defineProperty(exports, "useVaultValue", { enumerable: true, get: function () { return hooks_1.useVaultValue; } });
|
|
47
|
+
Object.defineProperty(exports, "useVaultJSON", { enumerable: true, get: function () { return hooks_1.useVaultJSON; } });
|
|
48
|
+
Object.defineProperty(exports, "useVaultStats", { enumerable: true, get: function () { return hooks_1.useVaultStats; } });
|
|
49
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;;;AAEH,OAAO;AACP,iCAAgC;AAAvB,8FAAA,KAAK,OAAA;AAEd,QAAQ;AACR,iCAMiB;AAFf,mGAAA,UAAU,OAAA;AACV,uGAAA,cAAc,OAAA;AAGhB,cAAc;AACd,iCAKiB;AAJf,iGAAA,QAAQ,OAAA;AACR,sGAAA,aAAa,OAAA;AACb,qGAAA,YAAY,OAAA;AACZ,sGAAA,aAAa,OAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-bridge.d.ts","sourceRoot":"","sources":["../src/native-bridge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAwBjD,eAAO,MAAM,YAAY;IACvB;;OAEG;qBACW,iBAAiB;CAGhC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NativeBridge = void 0;
|
|
4
|
+
const react_native_1 = require("react-native");
|
|
5
|
+
/**
|
|
6
|
+
* Get the native vault module for the current platform.
|
|
7
|
+
*
|
|
8
|
+
* Android: Uses JNI → Rust .so library
|
|
9
|
+
* iOS: Uses C FFI → Rust .a static library
|
|
10
|
+
* Web: Uses WASM → Rust compiled to WebAssembly
|
|
11
|
+
*/
|
|
12
|
+
function getNativeModule() {
|
|
13
|
+
const { RajeevVault } = react_native_1.NativeModules;
|
|
14
|
+
if (!RajeevVault) {
|
|
15
|
+
throw new Error(`@rajeev02/vault: Native module not found. ` +
|
|
16
|
+
`Make sure you have run 'pod install' (iOS) or rebuilt the app (Android).\n\n` +
|
|
17
|
+
`Platform: ${react_native_1.Platform.OS}\n` +
|
|
18
|
+
`If you're on web, import from '@rajeev02/vault/web' instead.`);
|
|
19
|
+
}
|
|
20
|
+
return RajeevVault;
|
|
21
|
+
}
|
|
22
|
+
exports.NativeBridge = {
|
|
23
|
+
/**
|
|
24
|
+
* Get the native module, lazy-loaded and cached
|
|
25
|
+
*/
|
|
26
|
+
get module() {
|
|
27
|
+
return getNativeModule();
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=native-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-bridge.js","sourceRoot":"","sources":["../src/native-bridge.ts"],"names":[],"mappings":";;;AAAA,+CAAuD;AAGvD;;;;;;GAMG;AACH,SAAS,eAAe;IACtB,MAAM,EAAE,WAAW,EAAE,GAAG,4BAAa,CAAC;IAEtC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,4CAA4C;YAC1C,8EAA8E;YAC9E,aAAa,uBAAQ,CAAC,EAAE,IAAI;YAC5B,8DAA8D,CACjE,CAAC;IACJ,CAAC;IAED,OAAO,WAAgC,CAAC;AAC1C,CAAC;AAEY,QAAA,YAAY,GAAG;IAC1B;;OAEG;IACH,IAAI,MAAM;QACR,OAAO,eAAe,EAAE,CAAC;IAC3B,CAAC;CACF,CAAC"}
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rajeev02/vault - Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Universal secure storage for React Native, Web, Watch, Auto, IoT
|
|
5
|
+
* Built by Rajeev Kumar Joshi (https://github.com/Rajeev02)
|
|
6
|
+
*/
|
|
7
|
+
/** Configuration for creating a vault instance */
|
|
8
|
+
export interface VaultConfig {
|
|
9
|
+
/** Unique app identifier (e.g., 'com.myapp') */
|
|
10
|
+
appId: string;
|
|
11
|
+
/** Custom database path. Defaults to app-specific location */
|
|
12
|
+
dbPath?: string;
|
|
13
|
+
/** Encryption algorithm. Default: 'AES-256-GCM' */
|
|
14
|
+
encryption?: 'AES-256-GCM';
|
|
15
|
+
/** Whether biometric auth is available on this device */
|
|
16
|
+
biometricAvailable?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Options when storing a value */
|
|
19
|
+
export interface StoreOptions {
|
|
20
|
+
/**
|
|
21
|
+
* When the value should expire and auto-delete.
|
|
22
|
+
* Format: "30m" (minutes), "24h" (hours), "7d" (days), "4w" (weeks)
|
|
23
|
+
* Default: null (never expires)
|
|
24
|
+
*/
|
|
25
|
+
expiry?: string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Whether biometric authentication is required to read this value.
|
|
28
|
+
* Default: false
|
|
29
|
+
*/
|
|
30
|
+
biometricRequired?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Whether this entry can be exported/backed up.
|
|
33
|
+
* Set to false for highly sensitive data like PINs.
|
|
34
|
+
* Default: true
|
|
35
|
+
*/
|
|
36
|
+
exportable?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Namespace to group related entries.
|
|
39
|
+
* e.g., 'auth', 'payments', 'health'
|
|
40
|
+
* Default: 'default'
|
|
41
|
+
*/
|
|
42
|
+
namespace?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Statistics about the vault */
|
|
45
|
+
export interface VaultStats {
|
|
46
|
+
/** Total number of stored entries */
|
|
47
|
+
totalEntries: number;
|
|
48
|
+
/** Number of distinct namespaces */
|
|
49
|
+
totalNamespaces: number;
|
|
50
|
+
/** Number of expired entries pending cleanup */
|
|
51
|
+
expiredEntries: number;
|
|
52
|
+
/** Approximate storage used in bytes */
|
|
53
|
+
storageBytes: number;
|
|
54
|
+
}
|
|
55
|
+
/** Error codes returned by the vault */
|
|
56
|
+
export declare enum VaultErrorCode {
|
|
57
|
+
ENCRYPTION_FAILED = "ENCRYPTION_FAILED",
|
|
58
|
+
DECRYPTION_FAILED = "DECRYPTION_FAILED",
|
|
59
|
+
KEY_NOT_FOUND = "KEY_NOT_FOUND",
|
|
60
|
+
KEY_EXPIRED = "KEY_EXPIRED",
|
|
61
|
+
STORAGE_ERROR = "STORAGE_ERROR",
|
|
62
|
+
INVALID_CONFIG = "INVALID_CONFIG",
|
|
63
|
+
BIOMETRIC_REQUIRED = "BIOMETRIC_REQUIRED",
|
|
64
|
+
BIOMETRIC_FAILED = "BIOMETRIC_FAILED",
|
|
65
|
+
NOT_INITIALIZED = "NOT_INITIALIZED"
|
|
66
|
+
}
|
|
67
|
+
/** Custom error class for vault operations */
|
|
68
|
+
export declare class VaultError extends Error {
|
|
69
|
+
readonly code: VaultErrorCode;
|
|
70
|
+
constructor(code: VaultErrorCode, message?: string);
|
|
71
|
+
}
|
|
72
|
+
/** Internal native module interface (platform-specific) */
|
|
73
|
+
export interface NativeVaultModule {
|
|
74
|
+
create(config: {
|
|
75
|
+
appId: string;
|
|
76
|
+
dbPath?: string;
|
|
77
|
+
encryptionAlgo: string;
|
|
78
|
+
biometricAvailable: boolean;
|
|
79
|
+
}): Promise<void>;
|
|
80
|
+
store(key: string, value: string, expiry: string | null, biometricRequired: boolean, exportable: boolean, namespace: string | null): Promise<void>;
|
|
81
|
+
retrieve(key: string, namespace: string | null): Promise<string | null>;
|
|
82
|
+
delete(key: string, namespace: string | null): Promise<boolean>;
|
|
83
|
+
exists(key: string, namespace: string | null): Promise<boolean>;
|
|
84
|
+
listKeys(namespace: string | null): Promise<string[]>;
|
|
85
|
+
listNamespaces(): Promise<string[]>;
|
|
86
|
+
wipeNamespace(namespace: string): Promise<void>;
|
|
87
|
+
wipeAll(): Promise<void>;
|
|
88
|
+
getStats(): Promise<VaultStats>;
|
|
89
|
+
cleanupExpired(): Promise<void>;
|
|
90
|
+
exportEntry(key: string, namespace: string | null): Promise<string | null>;
|
|
91
|
+
importEntry(key: string, encryptedData: string, expiry: string | null, biometricRequired: boolean, exportable: boolean, namespace: string | null): Promise<void>;
|
|
92
|
+
generateKey(): Promise<string>;
|
|
93
|
+
hash(input: string): Promise<string>;
|
|
94
|
+
verifyHash(input: string, hash: string): Promise<boolean>;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/lib/types.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @rajeev02/vault - Type Definitions
|
|
4
|
+
*
|
|
5
|
+
* Universal secure storage for React Native, Web, Watch, Auto, IoT
|
|
6
|
+
* Built by Rajeev Kumar Joshi (https://github.com/Rajeev02)
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.VaultError = exports.VaultErrorCode = void 0;
|
|
10
|
+
/** Error codes returned by the vault */
|
|
11
|
+
var VaultErrorCode;
|
|
12
|
+
(function (VaultErrorCode) {
|
|
13
|
+
VaultErrorCode["ENCRYPTION_FAILED"] = "ENCRYPTION_FAILED";
|
|
14
|
+
VaultErrorCode["DECRYPTION_FAILED"] = "DECRYPTION_FAILED";
|
|
15
|
+
VaultErrorCode["KEY_NOT_FOUND"] = "KEY_NOT_FOUND";
|
|
16
|
+
VaultErrorCode["KEY_EXPIRED"] = "KEY_EXPIRED";
|
|
17
|
+
VaultErrorCode["STORAGE_ERROR"] = "STORAGE_ERROR";
|
|
18
|
+
VaultErrorCode["INVALID_CONFIG"] = "INVALID_CONFIG";
|
|
19
|
+
VaultErrorCode["BIOMETRIC_REQUIRED"] = "BIOMETRIC_REQUIRED";
|
|
20
|
+
VaultErrorCode["BIOMETRIC_FAILED"] = "BIOMETRIC_FAILED";
|
|
21
|
+
VaultErrorCode["NOT_INITIALIZED"] = "NOT_INITIALIZED";
|
|
22
|
+
})(VaultErrorCode || (exports.VaultErrorCode = VaultErrorCode = {}));
|
|
23
|
+
/** Custom error class for vault operations */
|
|
24
|
+
class VaultError extends Error {
|
|
25
|
+
constructor(code, message) {
|
|
26
|
+
super(message || code);
|
|
27
|
+
this.name = 'VaultError';
|
|
28
|
+
this.code = code;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.VaultError = VaultError;
|
|
32
|
+
//# sourceMappingURL=types.js.map
|
package/lib/types.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AA8DH,wCAAwC;AACxC,IAAY,cAUX;AAVD,WAAY,cAAc;IACxB,yDAAuC,CAAA;IACvC,yDAAuC,CAAA;IACvC,iDAA+B,CAAA;IAC/B,6CAA2B,CAAA;IAC3B,iDAA+B,CAAA;IAC/B,mDAAiC,CAAA;IACjC,2DAAyC,CAAA;IACzC,uDAAqC,CAAA;IACrC,qDAAmC,CAAA;AACrC,CAAC,EAVW,cAAc,8BAAd,cAAc,QAUzB;AAED,8CAA8C;AAC9C,MAAa,UAAW,SAAQ,KAAK;IAGnC,YAAY,IAAoB,EAAE,OAAgB;QAChD,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AARD,gCAQC"}
|
package/lib/vault.d.ts
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { VaultConfig, StoreOptions, VaultStats } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* 🔐 Rajeev Vault — Universal Secure Storage
|
|
4
|
+
*
|
|
5
|
+
* AES-256-GCM encrypted storage that works across all platforms.
|
|
6
|
+
* Built with Rust core for maximum security and performance.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { Vault } from '@rajeev02/vault';
|
|
11
|
+
*
|
|
12
|
+
* // Create vault
|
|
13
|
+
* const vault = await Vault.create({ appId: 'com.myapp' });
|
|
14
|
+
*
|
|
15
|
+
* // Store securely
|
|
16
|
+
* await vault.set('token', 'my-secret-value', { expiry: '24h' });
|
|
17
|
+
*
|
|
18
|
+
* // Retrieve
|
|
19
|
+
* const token = await vault.get('token');
|
|
20
|
+
*
|
|
21
|
+
* // Use namespaces
|
|
22
|
+
* await vault.namespace('payments').set('upi_pin', '1234', {
|
|
23
|
+
* biometricRequired: true,
|
|
24
|
+
* exportable: false,
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @author Rajeev Kumar Joshi (https://github.com/Rajeev02)
|
|
29
|
+
*/
|
|
30
|
+
export declare class Vault {
|
|
31
|
+
private initialized;
|
|
32
|
+
private config;
|
|
33
|
+
private currentNamespace;
|
|
34
|
+
private constructor();
|
|
35
|
+
/**
|
|
36
|
+
* Create a new Vault instance.
|
|
37
|
+
* This is the main entry point — use this instead of `new Vault()`.
|
|
38
|
+
*
|
|
39
|
+
* @param config - Vault configuration
|
|
40
|
+
* @returns Initialized Vault instance
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const vault = await Vault.create({
|
|
45
|
+
* appId: 'com.myapp',
|
|
46
|
+
* biometricAvailable: true,
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
static create(config: VaultConfig): Promise<Vault>;
|
|
51
|
+
/**
|
|
52
|
+
* Ensure the vault is initialized before any operation
|
|
53
|
+
*/
|
|
54
|
+
private ensureInitialized;
|
|
55
|
+
/**
|
|
56
|
+
* Store a value securely.
|
|
57
|
+
*
|
|
58
|
+
* @param key - Unique key for the value
|
|
59
|
+
* @param value - String value to encrypt and store
|
|
60
|
+
* @param options - Storage options (expiry, biometric, namespace)
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* // Simple store
|
|
65
|
+
* await vault.set('token', 'eyJhbG...');
|
|
66
|
+
*
|
|
67
|
+
* // With expiry
|
|
68
|
+
* await vault.set('otp', '4829', { expiry: '5m' });
|
|
69
|
+
*
|
|
70
|
+
* // With biometric protection
|
|
71
|
+
* await vault.set('pin', '1234', {
|
|
72
|
+
* biometricRequired: true,
|
|
73
|
+
* exportable: false,
|
|
74
|
+
* });
|
|
75
|
+
*
|
|
76
|
+
* // Store JSON
|
|
77
|
+
* await vault.set('user', JSON.stringify({ name: 'Rajeev', role: 'admin' }));
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
set(key: string, value: string, options?: StoreOptions): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Retrieve a decrypted value.
|
|
83
|
+
* Returns null if the key doesn't exist or has expired.
|
|
84
|
+
*
|
|
85
|
+
* @param key - Key to retrieve
|
|
86
|
+
* @param namespace - Optional namespace override
|
|
87
|
+
* @returns Decrypted value or null
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* const token = await vault.get('token');
|
|
92
|
+
* if (token) {
|
|
93
|
+
* console.log('Got token:', token);
|
|
94
|
+
* } else {
|
|
95
|
+
* console.log('Token not found or expired');
|
|
96
|
+
* }
|
|
97
|
+
*
|
|
98
|
+
* // Get JSON
|
|
99
|
+
* const userJson = await vault.get('user');
|
|
100
|
+
* const user = userJson ? JSON.parse(userJson) : null;
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
get(key: string, namespace?: string): Promise<string | null>;
|
|
104
|
+
/**
|
|
105
|
+
* Get a value, parsed as JSON.
|
|
106
|
+
* Convenience method that combines get() + JSON.parse().
|
|
107
|
+
*
|
|
108
|
+
* @param key - Key to retrieve
|
|
109
|
+
* @param namespace - Optional namespace override
|
|
110
|
+
* @returns Parsed object or null
|
|
111
|
+
*/
|
|
112
|
+
getJSON<T = unknown>(key: string, namespace?: string): Promise<T | null>;
|
|
113
|
+
/**
|
|
114
|
+
* Store a value as JSON.
|
|
115
|
+
* Convenience method that combines JSON.stringify() + set().
|
|
116
|
+
*/
|
|
117
|
+
setJSON<T>(key: string, value: T, options?: StoreOptions): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Delete a key from the vault.
|
|
120
|
+
*
|
|
121
|
+
* @param key - Key to delete
|
|
122
|
+
* @param namespace - Optional namespace override
|
|
123
|
+
* @returns true if the key was deleted, false if not found
|
|
124
|
+
*/
|
|
125
|
+
delete(key: string, namespace?: string): Promise<boolean>;
|
|
126
|
+
/**
|
|
127
|
+
* Check if a key exists (and is not expired).
|
|
128
|
+
*
|
|
129
|
+
* @param key - Key to check
|
|
130
|
+
* @param namespace - Optional namespace override
|
|
131
|
+
* @returns true if key exists and is valid
|
|
132
|
+
*/
|
|
133
|
+
has(key: string, namespace?: string): Promise<boolean>;
|
|
134
|
+
/**
|
|
135
|
+
* Create a namespace-scoped vault view.
|
|
136
|
+
* All operations on the returned object are scoped to this namespace.
|
|
137
|
+
*
|
|
138
|
+
* @param name - Namespace name
|
|
139
|
+
* @returns Namespace-scoped vault
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* const payments = vault.namespace('payments');
|
|
144
|
+
* await payments.set('upi_pin', '1234');
|
|
145
|
+
* await payments.set('card_last4', '4242');
|
|
146
|
+
*
|
|
147
|
+
* const health = vault.namespace('health');
|
|
148
|
+
* await health.set('blood_group', 'O+');
|
|
149
|
+
*
|
|
150
|
+
* // Keys don't collide across namespaces
|
|
151
|
+
* await vault.namespace('a').set('key', 'value-a');
|
|
152
|
+
* await vault.namespace('b').set('key', 'value-b');
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
namespace(name: string): NamespacedVault;
|
|
156
|
+
/**
|
|
157
|
+
* List all keys in a namespace.
|
|
158
|
+
*
|
|
159
|
+
* @param namespace - Namespace to list (default namespace if not specified)
|
|
160
|
+
* @returns Array of key names
|
|
161
|
+
*/
|
|
162
|
+
keys(namespace?: string): Promise<string[]>;
|
|
163
|
+
/**
|
|
164
|
+
* List all namespaces in the vault.
|
|
165
|
+
*
|
|
166
|
+
* @returns Array of namespace names
|
|
167
|
+
*/
|
|
168
|
+
namespaces(): Promise<string[]>;
|
|
169
|
+
/**
|
|
170
|
+
* Delete all entries in a namespace.
|
|
171
|
+
*
|
|
172
|
+
* @param namespace - Namespace to wipe
|
|
173
|
+
*/
|
|
174
|
+
wipeNamespace(namespace: string): Promise<void>;
|
|
175
|
+
/**
|
|
176
|
+
* ⚠️ Delete EVERYTHING in the vault. Cannot be undone.
|
|
177
|
+
* Typically used during logout.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* async function logout() {
|
|
182
|
+
* await vault.wipeAll();
|
|
183
|
+
* navigation.reset('LoginScreen');
|
|
184
|
+
* }
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
wipeAll(): Promise<void>;
|
|
188
|
+
/**
|
|
189
|
+
* Get storage statistics.
|
|
190
|
+
*
|
|
191
|
+
* @returns Stats including entry count, namespaces, storage size
|
|
192
|
+
*/
|
|
193
|
+
stats(): Promise<VaultStats>;
|
|
194
|
+
/**
|
|
195
|
+
* Clean up expired entries. Call periodically to reclaim space.
|
|
196
|
+
*/
|
|
197
|
+
cleanup(): Promise<void>;
|
|
198
|
+
/**
|
|
199
|
+
* Generate a cryptographically secure random key.
|
|
200
|
+
* Useful for creating encryption keys, API tokens, etc.
|
|
201
|
+
*
|
|
202
|
+
* @returns Base64-encoded random key
|
|
203
|
+
*/
|
|
204
|
+
static generateKey(): Promise<string>;
|
|
205
|
+
/**
|
|
206
|
+
* Hash a value (one-way). Useful for storing passwords.
|
|
207
|
+
*
|
|
208
|
+
* @param input - Value to hash
|
|
209
|
+
* @returns Salted hash string
|
|
210
|
+
*/
|
|
211
|
+
static hash(input: string): Promise<string>;
|
|
212
|
+
/**
|
|
213
|
+
* Verify a value against a hash.
|
|
214
|
+
*
|
|
215
|
+
* @param input - Value to verify
|
|
216
|
+
* @param hash - Hash to verify against
|
|
217
|
+
* @returns true if the value matches the hash
|
|
218
|
+
*/
|
|
219
|
+
static verifyHash(input: string, hash: string): Promise<boolean>;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* A namespace-scoped view of the vault.
|
|
223
|
+
* All operations are automatically scoped to the namespace.
|
|
224
|
+
*/
|
|
225
|
+
declare class NamespacedVault {
|
|
226
|
+
private vault;
|
|
227
|
+
private name;
|
|
228
|
+
constructor(vault: Vault, name: string);
|
|
229
|
+
set(key: string, value: string, options?: Omit<StoreOptions, 'namespace'>): Promise<void>;
|
|
230
|
+
get(key: string): Promise<string | null>;
|
|
231
|
+
getJSON<T = unknown>(key: string): Promise<T | null>;
|
|
232
|
+
setJSON<T>(key: string, value: T, options?: Omit<StoreOptions, 'namespace'>): Promise<void>;
|
|
233
|
+
delete(key: string): Promise<boolean>;
|
|
234
|
+
has(key: string): Promise<boolean>;
|
|
235
|
+
keys(): Promise<string[]>;
|
|
236
|
+
wipe(): Promise<void>;
|
|
237
|
+
}
|
|
238
|
+
export {};
|
|
239
|
+
//# sourceMappingURL=vault.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"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"}
|
package/lib/vault.js
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Vault = void 0;
|
|
4
|
+
const native_bridge_1 = require("./native-bridge");
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
/**
|
|
7
|
+
* 🔐 Rajeev Vault — Universal Secure Storage
|
|
8
|
+
*
|
|
9
|
+
* AES-256-GCM encrypted storage that works across all platforms.
|
|
10
|
+
* Built with Rust core for maximum security and performance.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { Vault } from '@rajeev02/vault';
|
|
15
|
+
*
|
|
16
|
+
* // Create vault
|
|
17
|
+
* const vault = await Vault.create({ appId: 'com.myapp' });
|
|
18
|
+
*
|
|
19
|
+
* // Store securely
|
|
20
|
+
* await vault.set('token', 'my-secret-value', { expiry: '24h' });
|
|
21
|
+
*
|
|
22
|
+
* // Retrieve
|
|
23
|
+
* const token = await vault.get('token');
|
|
24
|
+
*
|
|
25
|
+
* // Use namespaces
|
|
26
|
+
* await vault.namespace('payments').set('upi_pin', '1234', {
|
|
27
|
+
* biometricRequired: true,
|
|
28
|
+
* exportable: false,
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @author Rajeev Kumar Joshi (https://github.com/Rajeev02)
|
|
33
|
+
*/
|
|
34
|
+
class Vault {
|
|
35
|
+
constructor(config) {
|
|
36
|
+
this.initialized = false;
|
|
37
|
+
this.currentNamespace = null;
|
|
38
|
+
this.config = config;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create a new Vault instance.
|
|
42
|
+
* This is the main entry point — use this instead of `new Vault()`.
|
|
43
|
+
*
|
|
44
|
+
* @param config - Vault configuration
|
|
45
|
+
* @returns Initialized Vault instance
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const vault = await Vault.create({
|
|
50
|
+
* appId: 'com.myapp',
|
|
51
|
+
* biometricAvailable: true,
|
|
52
|
+
* });
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
static async create(config) {
|
|
56
|
+
const vault = new Vault(config);
|
|
57
|
+
try {
|
|
58
|
+
await native_bridge_1.NativeBridge.module.create({
|
|
59
|
+
appId: config.appId,
|
|
60
|
+
dbPath: config.dbPath,
|
|
61
|
+
encryptionAlgo: config.encryption || 'AES-256-GCM',
|
|
62
|
+
biometricAvailable: config.biometricAvailable ?? false,
|
|
63
|
+
});
|
|
64
|
+
vault.initialized = true;
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw new types_1.VaultError(types_1.VaultErrorCode.INVALID_CONFIG, `Failed to initialize vault: ${error}`);
|
|
68
|
+
}
|
|
69
|
+
return vault;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Ensure the vault is initialized before any operation
|
|
73
|
+
*/
|
|
74
|
+
ensureInitialized() {
|
|
75
|
+
if (!this.initialized) {
|
|
76
|
+
throw new types_1.VaultError(types_1.VaultErrorCode.NOT_INITIALIZED, 'Vault not initialized. Call Vault.create() first.');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// ─── Core Operations ─────────────────────────────────────────────
|
|
80
|
+
/**
|
|
81
|
+
* Store a value securely.
|
|
82
|
+
*
|
|
83
|
+
* @param key - Unique key for the value
|
|
84
|
+
* @param value - String value to encrypt and store
|
|
85
|
+
* @param options - Storage options (expiry, biometric, namespace)
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* // Simple store
|
|
90
|
+
* await vault.set('token', 'eyJhbG...');
|
|
91
|
+
*
|
|
92
|
+
* // With expiry
|
|
93
|
+
* await vault.set('otp', '4829', { expiry: '5m' });
|
|
94
|
+
*
|
|
95
|
+
* // With biometric protection
|
|
96
|
+
* await vault.set('pin', '1234', {
|
|
97
|
+
* biometricRequired: true,
|
|
98
|
+
* exportable: false,
|
|
99
|
+
* });
|
|
100
|
+
*
|
|
101
|
+
* // Store JSON
|
|
102
|
+
* await vault.set('user', JSON.stringify({ name: 'Rajeev', role: 'admin' }));
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
async set(key, value, options) {
|
|
106
|
+
this.ensureInitialized();
|
|
107
|
+
const ns = options?.namespace ?? this.currentNamespace;
|
|
108
|
+
await native_bridge_1.NativeBridge.module.store(key, value, options?.expiry ?? null, options?.biometricRequired ?? false, options?.exportable ?? true, ns);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Retrieve a decrypted value.
|
|
112
|
+
* Returns null if the key doesn't exist or has expired.
|
|
113
|
+
*
|
|
114
|
+
* @param key - Key to retrieve
|
|
115
|
+
* @param namespace - Optional namespace override
|
|
116
|
+
* @returns Decrypted value or null
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* const token = await vault.get('token');
|
|
121
|
+
* if (token) {
|
|
122
|
+
* console.log('Got token:', token);
|
|
123
|
+
* } else {
|
|
124
|
+
* console.log('Token not found or expired');
|
|
125
|
+
* }
|
|
126
|
+
*
|
|
127
|
+
* // Get JSON
|
|
128
|
+
* const userJson = await vault.get('user');
|
|
129
|
+
* const user = userJson ? JSON.parse(userJson) : null;
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
async get(key, namespace) {
|
|
133
|
+
this.ensureInitialized();
|
|
134
|
+
const ns = namespace ?? this.currentNamespace;
|
|
135
|
+
return native_bridge_1.NativeBridge.module.retrieve(key, ns);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get a value, parsed as JSON.
|
|
139
|
+
* Convenience method that combines get() + JSON.parse().
|
|
140
|
+
*
|
|
141
|
+
* @param key - Key to retrieve
|
|
142
|
+
* @param namespace - Optional namespace override
|
|
143
|
+
* @returns Parsed object or null
|
|
144
|
+
*/
|
|
145
|
+
async getJSON(key, namespace) {
|
|
146
|
+
const raw = await this.get(key, namespace);
|
|
147
|
+
if (raw === null)
|
|
148
|
+
return null;
|
|
149
|
+
try {
|
|
150
|
+
return JSON.parse(raw);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Store a value as JSON.
|
|
158
|
+
* Convenience method that combines JSON.stringify() + set().
|
|
159
|
+
*/
|
|
160
|
+
async setJSON(key, value, options) {
|
|
161
|
+
const serialized = JSON.stringify(value);
|
|
162
|
+
await this.set(key, serialized, options);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Delete a key from the vault.
|
|
166
|
+
*
|
|
167
|
+
* @param key - Key to delete
|
|
168
|
+
* @param namespace - Optional namespace override
|
|
169
|
+
* @returns true if the key was deleted, false if not found
|
|
170
|
+
*/
|
|
171
|
+
async delete(key, namespace) {
|
|
172
|
+
this.ensureInitialized();
|
|
173
|
+
const ns = namespace ?? this.currentNamespace;
|
|
174
|
+
return native_bridge_1.NativeBridge.module.delete(key, ns);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Check if a key exists (and is not expired).
|
|
178
|
+
*
|
|
179
|
+
* @param key - Key to check
|
|
180
|
+
* @param namespace - Optional namespace override
|
|
181
|
+
* @returns true if key exists and is valid
|
|
182
|
+
*/
|
|
183
|
+
async has(key, namespace) {
|
|
184
|
+
this.ensureInitialized();
|
|
185
|
+
const ns = namespace ?? this.currentNamespace;
|
|
186
|
+
return native_bridge_1.NativeBridge.module.exists(key, ns);
|
|
187
|
+
}
|
|
188
|
+
// ─── Namespace Operations ─────────────────────────────────────────
|
|
189
|
+
/**
|
|
190
|
+
* Create a namespace-scoped vault view.
|
|
191
|
+
* All operations on the returned object are scoped to this namespace.
|
|
192
|
+
*
|
|
193
|
+
* @param name - Namespace name
|
|
194
|
+
* @returns Namespace-scoped vault
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* const payments = vault.namespace('payments');
|
|
199
|
+
* await payments.set('upi_pin', '1234');
|
|
200
|
+
* await payments.set('card_last4', '4242');
|
|
201
|
+
*
|
|
202
|
+
* const health = vault.namespace('health');
|
|
203
|
+
* await health.set('blood_group', 'O+');
|
|
204
|
+
*
|
|
205
|
+
* // Keys don't collide across namespaces
|
|
206
|
+
* await vault.namespace('a').set('key', 'value-a');
|
|
207
|
+
* await vault.namespace('b').set('key', 'value-b');
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
namespace(name) {
|
|
211
|
+
this.ensureInitialized();
|
|
212
|
+
return new NamespacedVault(this, name);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* List all keys in a namespace.
|
|
216
|
+
*
|
|
217
|
+
* @param namespace - Namespace to list (default namespace if not specified)
|
|
218
|
+
* @returns Array of key names
|
|
219
|
+
*/
|
|
220
|
+
async keys(namespace) {
|
|
221
|
+
this.ensureInitialized();
|
|
222
|
+
const ns = namespace ?? this.currentNamespace;
|
|
223
|
+
return native_bridge_1.NativeBridge.module.listKeys(ns);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* List all namespaces in the vault.
|
|
227
|
+
*
|
|
228
|
+
* @returns Array of namespace names
|
|
229
|
+
*/
|
|
230
|
+
async namespaces() {
|
|
231
|
+
this.ensureInitialized();
|
|
232
|
+
return native_bridge_1.NativeBridge.module.listNamespaces();
|
|
233
|
+
}
|
|
234
|
+
// ─── Destructive Operations ───────────────────────────────────────
|
|
235
|
+
/**
|
|
236
|
+
* Delete all entries in a namespace.
|
|
237
|
+
*
|
|
238
|
+
* @param namespace - Namespace to wipe
|
|
239
|
+
*/
|
|
240
|
+
async wipeNamespace(namespace) {
|
|
241
|
+
this.ensureInitialized();
|
|
242
|
+
await native_bridge_1.NativeBridge.module.wipeNamespace(namespace);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* ⚠️ Delete EVERYTHING in the vault. Cannot be undone.
|
|
246
|
+
* Typically used during logout.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```typescript
|
|
250
|
+
* async function logout() {
|
|
251
|
+
* await vault.wipeAll();
|
|
252
|
+
* navigation.reset('LoginScreen');
|
|
253
|
+
* }
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
async wipeAll() {
|
|
257
|
+
this.ensureInitialized();
|
|
258
|
+
await native_bridge_1.NativeBridge.module.wipeAll();
|
|
259
|
+
}
|
|
260
|
+
// ─── Utility Operations ───────────────────────────────────────────
|
|
261
|
+
/**
|
|
262
|
+
* Get storage statistics.
|
|
263
|
+
*
|
|
264
|
+
* @returns Stats including entry count, namespaces, storage size
|
|
265
|
+
*/
|
|
266
|
+
async stats() {
|
|
267
|
+
this.ensureInitialized();
|
|
268
|
+
return native_bridge_1.NativeBridge.module.getStats();
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Clean up expired entries. Call periodically to reclaim space.
|
|
272
|
+
*/
|
|
273
|
+
async cleanup() {
|
|
274
|
+
this.ensureInitialized();
|
|
275
|
+
await native_bridge_1.NativeBridge.module.cleanupExpired();
|
|
276
|
+
}
|
|
277
|
+
// ─── Static Utilities ─────────────────────────────────────────────
|
|
278
|
+
/**
|
|
279
|
+
* Generate a cryptographically secure random key.
|
|
280
|
+
* Useful for creating encryption keys, API tokens, etc.
|
|
281
|
+
*
|
|
282
|
+
* @returns Base64-encoded random key
|
|
283
|
+
*/
|
|
284
|
+
static async generateKey() {
|
|
285
|
+
return native_bridge_1.NativeBridge.module.generateKey();
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Hash a value (one-way). Useful for storing passwords.
|
|
289
|
+
*
|
|
290
|
+
* @param input - Value to hash
|
|
291
|
+
* @returns Salted hash string
|
|
292
|
+
*/
|
|
293
|
+
static async hash(input) {
|
|
294
|
+
return native_bridge_1.NativeBridge.module.hash(input);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Verify a value against a hash.
|
|
298
|
+
*
|
|
299
|
+
* @param input - Value to verify
|
|
300
|
+
* @param hash - Hash to verify against
|
|
301
|
+
* @returns true if the value matches the hash
|
|
302
|
+
*/
|
|
303
|
+
static async verifyHash(input, hash) {
|
|
304
|
+
return native_bridge_1.NativeBridge.module.verifyHash(input, hash);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
exports.Vault = Vault;
|
|
308
|
+
/**
|
|
309
|
+
* A namespace-scoped view of the vault.
|
|
310
|
+
* All operations are automatically scoped to the namespace.
|
|
311
|
+
*/
|
|
312
|
+
class NamespacedVault {
|
|
313
|
+
constructor(vault, name) {
|
|
314
|
+
this.vault = vault;
|
|
315
|
+
this.name = name;
|
|
316
|
+
}
|
|
317
|
+
async set(key, value, options) {
|
|
318
|
+
return this.vault.set(key, value, { ...options, namespace: this.name });
|
|
319
|
+
}
|
|
320
|
+
async get(key) {
|
|
321
|
+
return this.vault.get(key, this.name);
|
|
322
|
+
}
|
|
323
|
+
async getJSON(key) {
|
|
324
|
+
return this.vault.getJSON(key, this.name);
|
|
325
|
+
}
|
|
326
|
+
async setJSON(key, value, options) {
|
|
327
|
+
return this.vault.setJSON(key, value, { ...options, namespace: this.name });
|
|
328
|
+
}
|
|
329
|
+
async delete(key) {
|
|
330
|
+
return this.vault.delete(key, this.name);
|
|
331
|
+
}
|
|
332
|
+
async has(key) {
|
|
333
|
+
return this.vault.has(key, this.name);
|
|
334
|
+
}
|
|
335
|
+
async keys() {
|
|
336
|
+
return this.vault.keys(this.name);
|
|
337
|
+
}
|
|
338
|
+
async wipe() {
|
|
339
|
+
return this.vault.wipeNamespace(this.name);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
//# sourceMappingURL=vault.js.map
|
package/lib/vault.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vault.js","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":";;;AAAA,mDAA+C;AAC/C,mCAMiB;AAEjB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAa,KAAK;IAKhB,YAAoB,MAAmB;QAJ/B,gBAAW,GAAG,KAAK,CAAC;QAEpB,qBAAgB,GAAkB,IAAI,CAAC;QAG7C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAmB;QACrC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;QAEhC,IAAI,CAAC;YACH,MAAM,4BAAY,CAAC,MAAM,CAAC,MAAM,CAAC;gBAC/B,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,cAAc,EAAE,MAAM,CAAC,UAAU,IAAI,aAAa;gBAClD,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,IAAI,KAAK;aACvD,CAAC,CAAC;YACH,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,kBAAU,CAClB,sBAAc,CAAC,cAAc,EAC7B,+BAA+B,KAAK,EAAE,CACvC,CAAC;QACJ,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACK,iBAAiB;QACvB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,IAAI,kBAAU,CAClB,sBAAc,CAAC,eAAe,EAC9B,mDAAmD,CACpD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,oEAAoE;IAEpE;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,OAAsB;QAC1D,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEzB,MAAM,EAAE,GAAG,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC,gBAAgB,CAAC;QAEvD,MAAM,4BAAY,CAAC,MAAM,CAAC,KAAK,CAC7B,GAAG,EACH,KAAK,EACL,OAAO,EAAE,MAAM,IAAI,IAAI,EACvB,OAAO,EAAE,iBAAiB,IAAI,KAAK,EACnC,OAAO,EAAE,UAAU,IAAI,IAAI,EAC3B,EAAE,CACH,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,SAAkB;QACvC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,MAAM,EAAE,GAAG,SAAS,IAAI,IAAI,CAAC,gBAAgB,CAAC;QAC9C,OAAO,4BAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,OAAO,CAAc,GAAW,EAAE,SAAkB;QACxD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC3C,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAE9B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAI,GAAW,EAAE,KAAQ,EAAE,OAAsB;QAC5D,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,CAAC,GAAW,EAAE,SAAkB;QAC1C,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,MAAM,EAAE,GAAG,SAAS,IAAI,IAAI,CAAC,gBAAgB,CAAC;QAC9C,OAAO,4BAAY,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,SAAkB;QACvC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,MAAM,EAAE,GAAG,SAAS,IAAI,IAAI,CAAC,gBAAgB,CAAC;QAC9C,OAAO,4BAAY,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,qEAAqE;IAErE;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,CAAC,IAAY;QACpB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,OAAO,IAAI,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,IAAI,CAAC,SAAkB;QAC3B,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,MAAM,EAAE,GAAG,SAAS,IAAI,IAAI,CAAC,gBAAgB,CAAC;QAC9C,OAAO,4BAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAU;QACd,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,OAAO,4BAAY,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IAC9C,CAAC;IAED,qEAAqE;IAErE;;;;OAIG;IACH,KAAK,CAAC,aAAa,CAAC,SAAiB;QACnC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,MAAM,4BAAY,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IACrD,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,MAAM,4BAAY,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IACtC,CAAC;IAED,qEAAqE;IAErE;;;;OAIG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,OAAO,4BAAY,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,MAAM,4BAAY,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;IAC7C,CAAC;IAED,qEAAqE;IAErE;;;;;OAKG;IACH,MAAM,CAAC,KAAK,CAAC,WAAW;QACtB,OAAO,4BAAY,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;IAC3C,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAa;QAC7B,OAAO,4BAAY,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,KAAa,EAAE,IAAY;QACjD,OAAO,4BAAY,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACrD,CAAC;CACF;AAxTD,sBAwTC;AAED;;;GAGG;AACH,MAAM,eAAe;IAInB,YAAY,KAAY,EAAE,IAAY;QACpC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,OAAyC;QAC7E,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,OAAO,CAAc,GAAW;QACpC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAI,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,OAAO,CAAI,GAAW,EAAE,KAAQ,EAAE,OAAyC;QAC/E,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rajeev02/vault",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Universal secure storage for React Native — AES-256-GCM encrypted, cross-platform",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"clean": "rm -rf lib",
|
|
10
|
+
"test": "jest",
|
|
11
|
+
"prepublishOnly": "yarn build"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"react-native",
|
|
15
|
+
"secure-storage",
|
|
16
|
+
"encryption",
|
|
17
|
+
"vault",
|
|
18
|
+
"keychain",
|
|
19
|
+
"keystore",
|
|
20
|
+
"cross-platform"
|
|
21
|
+
],
|
|
22
|
+
"author": "Rajeev Kumar Joshi <rajeevjoshi91@gmail.com> (https://rajeev02.github.io)",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/Rajeev02/rajeev-sdk",
|
|
27
|
+
"directory": "packages/vault"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"react": ">=18.3.0",
|
|
31
|
+
"react-native": ">=0.84.0",
|
|
32
|
+
"expo": ">=54.0.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependenciesMeta": {
|
|
35
|
+
"expo": {
|
|
36
|
+
"optional": true
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/react": "^19.0.0",
|
|
41
|
+
"typescript": "^5.4.0",
|
|
42
|
+
"jest": "^29.0.0",
|
|
43
|
+
"@types/jest": "^29.0.0"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"lib/",
|
|
47
|
+
"android/",
|
|
48
|
+
"ios/",
|
|
49
|
+
"rust-core/",
|
|
50
|
+
"rajeev-vault.podspec",
|
|
51
|
+
"README.md"
|
|
52
|
+
],
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://github.com/Rajeev02/rajeev-sdk#readme",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/Rajeev02/rajeev-sdk/issues"
|
|
59
|
+
}
|
|
60
|
+
}
|