Restructure files
This commit is contained in:
parent
b4b21561ed
commit
bdd46a530c
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
drafts
|
||||
node_modules
|
||||
mochawesome-report
|
||||
lib
|
||||
|
@ -39,6 +39,7 @@
|
||||
"sass": "^1.43.2",
|
||||
"sinon": "^11.1.2",
|
||||
"sinon-chai": "^3.7.0",
|
||||
"tslib": "^2.3.1",
|
||||
"ts-node": "^10.2.1",
|
||||
"tsconfig-paths": "^3.11.0",
|
||||
"typescript": "^4.4.3"
|
||||
|
6
packages/adapters/package.json
Normal file
6
packages/adapters/package.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "opvault-adapters",
|
||||
"dependencies": {
|
||||
"opvault.js": "*"
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { Buffer } from "buffer"
|
||||
import type { IAdapter, IFileSystem } from "./index"
|
||||
import type { IAdapter, IFileSystem } from "opvault.js/src/adapter"
|
||||
|
||||
function normalize(path: string) {
|
||||
return path.replace(/^\//, "")
|
@ -1,5 +1,5 @@
|
||||
import { Buffer } from "buffer"
|
||||
import type { IAdapter, IFileSystem } from "./index"
|
||||
import type { IAdapter, IFileSystem } from "opvault.js/src/adapter"
|
||||
|
||||
export class FileSystem implements IFileSystem {
|
||||
private paths = new Set<string>()
|
@ -4,7 +4,7 @@
|
||||
"version": "0.0.1",
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"build": "rollup -c; cp src/adapters/index.d.ts lib/adapters/; prettier --write lib >/dev/null",
|
||||
"build": "rollup -c; prettier --write lib >/dev/null",
|
||||
"build:docs": "typedoc --out docs src/index.ts --excludePrivate"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -8,7 +8,6 @@ import { dependencies } from "./package.json"
|
||||
export default () => ({
|
||||
input: {
|
||||
index: "./src/index.ts",
|
||||
"adapters/node": "./src/adapters/node.ts",
|
||||
},
|
||||
external: builtinModules.concat(Object.keys(dependencies)),
|
||||
output: {
|
||||
|
@ -1,3 +1,6 @@
|
||||
import { promises as fs, existsSync } from "fs"
|
||||
import { webcrypto } from "crypto"
|
||||
|
||||
/**
|
||||
* An object that implements basic file system functionalities.
|
||||
*/
|
||||
@ -53,3 +56,19 @@ export interface IAdapter {
|
||||
*/
|
||||
subtle: SubtleCrypto
|
||||
}
|
||||
|
||||
/**
|
||||
* Default Node.js adapter. This can be used while using `opvault.js`
|
||||
* in a Node.js environment.
|
||||
*/
|
||||
export const nodeAdapter: IAdapter = {
|
||||
fs: {
|
||||
readFile: path => fs.readFile(path, "utf-8"),
|
||||
readBuffer: path => fs.readFile(path),
|
||||
writeFile: fs.writeFile,
|
||||
readdir: fs.readdir,
|
||||
isDirectory: async path => fs.stat(path).then(x => x.isDirectory()),
|
||||
exists: async path => existsSync(path),
|
||||
},
|
||||
subtle: (webcrypto as any).subtle,
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import { promises as fs, existsSync } from "fs"
|
||||
import { webcrypto } from "crypto"
|
||||
|
||||
import type { IAdapter } from "./index"
|
||||
|
||||
/**
|
||||
* Default Node.js adapter. This can be used while using `opvault.js`
|
||||
* in a Node.js environment.
|
||||
*/
|
||||
export const nodeAdapter: IAdapter = {
|
||||
fs: {
|
||||
readFile: path => fs.readFile(path, "utf-8"),
|
||||
readBuffer: path => fs.readFile(path),
|
||||
writeFile: fs.writeFile,
|
||||
readdir: fs.readdir,
|
||||
isDirectory: async path => fs.stat(path).then(x => x.isDirectory()),
|
||||
exists: async path => existsSync(path),
|
||||
},
|
||||
subtle: (webcrypto as any).subtle,
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
import { Buffer } from "buffer"
|
||||
import { decryptData } from "../decipher"
|
||||
import type { IAdapter } from "../adapters"
|
||||
import { createEventEmitter } from "../ee"
|
||||
import { HMACAssertionError } from "../errors"
|
||||
import type { i18n } from "../i18n"
|
||||
import type { ItemDetails, Overview, Profile } from "../types"
|
||||
import { setIfAbsent } from "../util"
|
||||
import type { EncryptedItem } from "./item"
|
||||
import { decryptData } from "./decipher"
|
||||
import type { IAdapter } from "./adapter"
|
||||
import { createEventEmitter } from "./ee"
|
||||
import { HMACAssertionError } from "./errors"
|
||||
import type { i18n } from "./i18n"
|
||||
import type { ItemDetails, Overview, Profile } from "./types"
|
||||
import { setIfAbsent } from "./util"
|
||||
import type { EncryptedItem } from "./models/item"
|
||||
|
||||
/** Encryption and MAC */
|
||||
export interface Cipher {
|
@ -1,6 +1,6 @@
|
||||
import { resolve, extname, basename } from "path"
|
||||
import invariant from "tiny-invariant"
|
||||
import type { IFileSystem } from "./adapters"
|
||||
import type { IFileSystem } from "./adapter"
|
||||
import { once } from "./util"
|
||||
|
||||
export type OnePasswordFileManager = ReturnType<typeof OnePasswordFileManager>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { resolve } from "path"
|
||||
import { Vault } from "./models/vault"
|
||||
import type { IAdapter } from "./adapters"
|
||||
import type { IAdapter } from "./adapter"
|
||||
import { asyncMap } from "./util"
|
||||
|
||||
export type { Vault } from "./models/vault"
|
||||
@ -18,7 +18,7 @@ interface IOptions {
|
||||
/**
|
||||
* Adapter used to interact with the file system and cryptography modules
|
||||
*/
|
||||
adapter?: IAdapter
|
||||
adapter?: IAdapter | Promise<IAdapter>
|
||||
}
|
||||
|
||||
/**
|
||||
@ -26,11 +26,11 @@ interface IOptions {
|
||||
*/
|
||||
export class OnePassword {
|
||||
readonly #path: string
|
||||
readonly #adapter: IAdapter
|
||||
readonly #adapter: IAdapter | Promise<IAdapter>
|
||||
|
||||
constructor({
|
||||
path,
|
||||
adapter = process.browser ? null : require("./adapters/node").nodeAdapter,
|
||||
adapter = process.browser ? null! : import("./adapter").then(x => x.nodeAdapter),
|
||||
}: IOptions) {
|
||||
this.#adapter = adapter
|
||||
this.#path = path
|
||||
@ -40,11 +40,11 @@ export class OnePassword {
|
||||
* @returns A list of names of profiles of the current vault.
|
||||
*/
|
||||
async getProfileNames() {
|
||||
const [fs, path] = [this.#adapter.fs, this.#path]
|
||||
const children = await fs.readdir(path)
|
||||
const { fs } = await this.#adapter
|
||||
const children = await fs.readdir(this.#path)
|
||||
const profiles: string[] = []
|
||||
await asyncMap(children, async child => {
|
||||
const fullPath = resolve(path, child)
|
||||
const fullPath = resolve(this.#path, child)
|
||||
if (
|
||||
(await fs.isDirectory(fullPath)) &&
|
||||
(await fs.exists(resolve(fullPath, "profile.js")))
|
||||
@ -59,6 +59,6 @@ export class OnePassword {
|
||||
* @returns A OnePassword Vault instance.
|
||||
*/
|
||||
async getProfile(profileName: string) {
|
||||
return await Vault.of(this.#path, profileName, this.#adapter)
|
||||
return await Vault.of(this.#path, profileName, await this.#adapter)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Buffer } from "buffer"
|
||||
import type { Crypto } from "./crypto"
|
||||
import type { Crypto } from "../crypto"
|
||||
import { invariant } from "../errors"
|
||||
|
||||
type integer = number
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { ItemDetails, Overview } from "../types"
|
||||
import type { Crypto } from "./crypto"
|
||||
import type { Crypto } from "../crypto"
|
||||
import { Attachment } from "./attachment"
|
||||
import { NotUnlockedError } from "../errors"
|
||||
import type { Category } from "../models"
|
||||
@ -8,7 +8,7 @@ export interface EncryptedItem {
|
||||
category: string // "001"
|
||||
/** Unix seconds */
|
||||
created: integer
|
||||
d: string // "b3BkYXRhMbt"
|
||||
d: string // details, bass64
|
||||
folder: string // 32 chars
|
||||
hmac: string // base64
|
||||
k: string // base64
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { IAdapter } from "../adapters"
|
||||
import type { IAdapter } from "../adapter"
|
||||
import { HMACAssertionError, invariant } from "../errors"
|
||||
import { OnePasswordFileManager } from "../fs"
|
||||
import { i18n } from "../i18n"
|
||||
import type { EncryptedItem } from "./item"
|
||||
import { Crypto } from "./crypto"
|
||||
import { Crypto } from "../crypto"
|
||||
import { Item } from "./item"
|
||||
import type { Profile } from "../types"
|
||||
import { WeakValueMap } from "../weakMap"
|
||||
|
@ -130,6 +130,9 @@ export interface ItemDetails {
|
||||
}[]
|
||||
/** Web form fields */
|
||||
fields?: ItemField[]
|
||||
/** Plain password items */
|
||||
backupKeys?: string[]
|
||||
password?: string
|
||||
}
|
||||
|
||||
export interface Folder {
|
||||
|
@ -12,8 +12,13 @@ fs.writeFileSync(
|
||||
dtsPath,
|
||||
`type Translation = Record<string, string>;
|
||||
declare const exportee: {
|
||||
${Object.keys(json)
|
||||
.map(x => `${x}: Translation;`)
|
||||
${Object.entries(json)
|
||||
.map(
|
||||
([category, value]) =>
|
||||
`${category}: {\n${Object.keys(value)
|
||||
.map(key => ` ${key}: Translation;`)
|
||||
.join("\n")}\n };`
|
||||
)
|
||||
.join("\n ")}
|
||||
};
|
||||
export default exportee;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import styled from "@emotion/styled"
|
||||
import type { Attachment, AttachmentMetadata, Item, ItemField } from "opvault.js"
|
||||
import { useEffect, useState } from "react"
|
||||
import type { ItemDetails } from "opvault.js/src/types"
|
||||
import { memo, useEffect, useState } from "react"
|
||||
import { useTranslate } from "../i18n"
|
||||
import { CategoryIcon } from "./CategoryIcon"
|
||||
import { ItemDates } from "./ItemDates"
|
||||
@ -10,6 +11,7 @@ import {
|
||||
FieldTitle,
|
||||
ItemDetailsFieldView,
|
||||
} from "./ItemField"
|
||||
import { PasswordFieldView } from "./ItemFieldValue"
|
||||
import { ItemWarning } from "./ItemWarning"
|
||||
|
||||
interface ItemViewProps {
|
||||
@ -57,6 +59,22 @@ const AttachmentContainer = styled.div`
|
||||
margin: 5px 0;
|
||||
`
|
||||
|
||||
const SectionsView: React.FC<{ sections?: ItemDetails["sections"] }> = ({ sections }) =>
|
||||
sections?.length ? (
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
{sections
|
||||
.filter(s => s.fields?.some(x => x.v != null))
|
||||
.map((section, i) => (
|
||||
<div key={i}>
|
||||
{section.title != null && <SectionTitle>{section.title}</SectionTitle>}
|
||||
{section.fields?.map((field, j) => (
|
||||
<ItemFieldView key={j} field={field} />
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : null
|
||||
|
||||
const FieldsView: React.FC<{ fields?: ItemField[] }> = ({ fields }) =>
|
||||
fields?.length ? (
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
@ -71,7 +89,7 @@ const TagsView: React.FC<{ tags?: string[] }> = ({ tags }) => {
|
||||
if (!tags?.length) return null
|
||||
return (
|
||||
<ExtraField>
|
||||
<FieldTitle>{t.noun_tags}</FieldTitle>
|
||||
<FieldTitle>{t.noun.tags}</FieldTitle>
|
||||
<div>
|
||||
{tags.map((tag, i) => (
|
||||
<Tag key={i}>{tag}</Tag>
|
||||
@ -81,7 +99,18 @@ const TagsView: React.FC<{ tags?: string[] }> = ({ tags }) => {
|
||||
)
|
||||
}
|
||||
|
||||
export const ItemView: React.FC<ItemViewProps> = ({ className, item }) => (
|
||||
const JSONView = memo<{ item: Item }>(({ item }) => (
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
<pre>
|
||||
{JSON.stringify({ overview: item.overview, details: item.details }, null, 2)}
|
||||
</pre>
|
||||
</details>
|
||||
))
|
||||
|
||||
export const ItemView: React.FC<ItemViewProps> = ({ className, item }) => {
|
||||
const t = useTranslate()
|
||||
return (
|
||||
<Container className={className}>
|
||||
<Inner>
|
||||
<ItemWarning item={item} />
|
||||
@ -90,26 +119,11 @@ export const ItemView: React.FC<ItemViewProps> = ({ className, item }) => (
|
||||
<Icon category={item.category} />
|
||||
<ItemTitle>{item.overview.title}</ItemTitle>
|
||||
</Header>
|
||||
<details>
|
||||
<summary>JSON</summary>
|
||||
<pre>
|
||||
{JSON.stringify({ overview: item.overview, details: item.details }, null, 2)}
|
||||
</pre>
|
||||
</details>
|
||||
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
{item.details.sections
|
||||
?.filter(s => s.fields?.some(x => x.v != null))
|
||||
.map((section, i) => (
|
||||
<div key={i}>
|
||||
{section.title != null && <SectionTitle>{section.title}</SectionTitle>}
|
||||
{section.fields?.map((field, j) => (
|
||||
<ItemFieldView key={j} field={field} />
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<JSONView item={item} />
|
||||
<div style={{ height: 10 }}></div>
|
||||
|
||||
<SectionsView sections={item.details.sections} />
|
||||
<FieldsView fields={item.details.fields} />
|
||||
|
||||
{item.details.notesPlain != null && (
|
||||
@ -121,6 +135,13 @@ export const ItemView: React.FC<ItemViewProps> = ({ className, item }) => (
|
||||
</ExtraField>
|
||||
)}
|
||||
|
||||
{item.details.password != null && (
|
||||
<ExtraField>
|
||||
<FieldTitle>{t.label.password}</FieldTitle>
|
||||
<PasswordFieldView field={{ v: item.details.password }} />
|
||||
</ExtraField>
|
||||
)}
|
||||
|
||||
<TagsView tags={item.overview.tags} />
|
||||
|
||||
{item.attachments.length > 0 && (
|
||||
@ -140,6 +161,7 @@ export const ItemView: React.FC<ItemViewProps> = ({ className, item }) => (
|
||||
</Inner>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
function AttachmentView({ file }: { file: Attachment }) {
|
||||
const [metadata, setMetadata] = useState<AttachmentMetadata>()
|
||||
|
@ -14,10 +14,10 @@ export const ItemDates: React.FC<{ item: Item }> = ({ item }) => {
|
||||
return (
|
||||
<Container>
|
||||
<div>
|
||||
{t.label_last_updated}: {new Date(item.updatedAt).toLocaleString()}
|
||||
{t.label.last_updated}: {new Date(item.updatedAt).toLocaleString()}
|
||||
</div>
|
||||
<div>
|
||||
{t.label_created_at}: {new Date(item.createdAt).toLocaleString()}
|
||||
{t.label.created_at}: {new Date(item.createdAt).toLocaleString()}
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
|
@ -7,7 +7,7 @@ const Container = styled.menu`
|
||||
box-shadow: #0004 0px 1px 4px;
|
||||
left: 99%;
|
||||
margin-block-start: 0;
|
||||
min-width: 120px;
|
||||
min-width: 150px;
|
||||
padding-inline-start: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -39,6 +39,7 @@ const Item = styled.div`
|
||||
height: 2.5em;
|
||||
align-items: center;
|
||||
padding-left: 1em;
|
||||
padding-right: 5px;
|
||||
position: relative;
|
||||
&:hover {
|
||||
background-color: #ddd;
|
||||
|
@ -9,8 +9,10 @@ import { useItemFieldContextMenu } from "./ItemFieldContextMenu"
|
||||
|
||||
const Container = styled.div``
|
||||
|
||||
export { Password as PasswordFieldView }
|
||||
|
||||
const Password: React.FC<{
|
||||
field: ItemSection.Concealed
|
||||
field: Pick<ItemSection.Concealed, "v">
|
||||
}> = ({ field }) => {
|
||||
const [show, setShow] = useState(false)
|
||||
const [bigText, showBigText] = useState(false)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createContext, memo, useContext, useEffect, useMemo, useState } from "react"
|
||||
import texts from "./texts.yml"
|
||||
|
||||
type Keys = keyof typeof texts
|
||||
const categories = Object.keys(texts)
|
||||
|
||||
const ALLOWED = new Set(["en", "fr"])
|
||||
const LOCALSTORAGE_KEY = "preferred-locale"
|
||||
@ -40,21 +40,29 @@ export function useTranslate() {
|
||||
const { locale } = useContext(LocaleContext)
|
||||
const t = useMemo(
|
||||
() =>
|
||||
Object.fromEntries(
|
||||
categories.map(category => [
|
||||
category,
|
||||
new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_, p: string) {
|
||||
const obj = (texts as any)[category]
|
||||
if (
|
||||
process.env.NODE_ENV === "development" &&
|
||||
!Object.prototype.hasOwnProperty.call(texts, p)
|
||||
!Object.prototype.hasOwnProperty.call(obj, p)
|
||||
) {
|
||||
throw new Error(`t.${p} does not exist.`)
|
||||
}
|
||||
return (texts as any)[p][locale]
|
||||
return obj[p][locale]
|
||||
},
|
||||
}
|
||||
),
|
||||
])
|
||||
) as {
|
||||
[key in Keys]: string
|
||||
[category in keyof typeof texts]: {
|
||||
[key in keyof typeof texts[category]]: string
|
||||
}
|
||||
},
|
||||
[locale]
|
||||
)
|
||||
|
@ -1,40 +1,51 @@
|
||||
# /* spellchecker: disable */
|
||||
label_choose_a_vault:
|
||||
label:
|
||||
choose_a_vault:
|
||||
en: Pick a vault
|
||||
fr: Choisir un coffre
|
||||
|
||||
label_no_vault_selected:
|
||||
no_vault_selected:
|
||||
en: No vault is selected.
|
||||
fr: Aucun coffre n’est sélectionné.
|
||||
|
||||
label_last_updated:
|
||||
last_updated:
|
||||
en: Last Updated
|
||||
fr: Dernière modification
|
||||
|
||||
label_created_at:
|
||||
created_at:
|
||||
en: Created At
|
||||
fr: Créé
|
||||
|
||||
label_password_placeholder:
|
||||
password_placeholder:
|
||||
en: Master Password
|
||||
fr: Mot de passe principal
|
||||
|
||||
noun_vault:
|
||||
username:
|
||||
en: Username
|
||||
fr: Nom d’utilisateur
|
||||
|
||||
password:
|
||||
en: Password
|
||||
fr: Mot de passe
|
||||
|
||||
noun:
|
||||
vault:
|
||||
en: vault
|
||||
fr: coffre
|
||||
|
||||
noun_tags:
|
||||
tags:
|
||||
en: tags
|
||||
fr: mots-clés
|
||||
|
||||
action_lock:
|
||||
action:
|
||||
lock:
|
||||
en: Lock
|
||||
fr: Vérouiller
|
||||
|
||||
action_unlock:
|
||||
unlock:
|
||||
en: Unlock
|
||||
fr: Déverouiller
|
||||
|
||||
action_go_back:
|
||||
go_back:
|
||||
en: Back
|
||||
fr: Revenir
|
||||
|
@ -102,17 +102,17 @@ export const Unlock: React.FC<{
|
||||
return (
|
||||
<Container>
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<BackButton onClick={onReturn} title={t.action_go_back}>
|
||||
<BackButton onClick={onReturn} title={t.action.go_back}>
|
||||
<IoMdArrowRoundBack />
|
||||
</BackButton>
|
||||
<Select
|
||||
title={t.noun_vault}
|
||||
title={t.noun.vault}
|
||||
value={profile}
|
||||
onChange={e => setProfile(e.currentTarget.value)}
|
||||
>
|
||||
{profiles.map(p => (
|
||||
<option key={p} value={p}>
|
||||
{t.noun_vault}: {p}
|
||||
{t.noun.vault}: {p}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
@ -122,14 +122,14 @@ export const Unlock: React.FC<{
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={e => setPassword(e.currentTarget.value)}
|
||||
placeholder={t.label_password_placeholder}
|
||||
placeholder={t.label.password_placeholder}
|
||||
onKeyUp={onKeyUp}
|
||||
/>
|
||||
<Submit
|
||||
type="submit"
|
||||
disabled={!profile || !password}
|
||||
onClick={unlock}
|
||||
title={t.action_unlock}
|
||||
title={t.action.unlock}
|
||||
>
|
||||
<FaUnlock />
|
||||
</Submit>
|
||||
|
@ -88,25 +88,36 @@ export const VaultView: React.FC<{ vault: Vault; onLock(): void }> = ({
|
||||
arrayFrom(vault.values()).then(setItems)
|
||||
}, [vault])
|
||||
|
||||
const filtered = useMemo(
|
||||
() =>
|
||||
sortedItem
|
||||
.filter(x => x.category !== Category.Tombstone)
|
||||
.filter(
|
||||
search
|
||||
? x =>
|
||||
stringCompare(search, x.overview.title) ||
|
||||
const filtered = useMemo(() => {
|
||||
const items = sortedItem.filter(x => x.category !== Category.Tombstone)
|
||||
let res: Item[] = items
|
||||
if (search) {
|
||||
res = []
|
||||
for (const x of items) {
|
||||
const compare = Math.max(
|
||||
stringCompare(search, x.overview.title),
|
||||
stringCompare(search, x.overview.ainfo)
|
||||
: () => true
|
||||
),
|
||||
[sortedItem, search]
|
||||
)
|
||||
) as CompareResult
|
||||
switch (compare) {
|
||||
case CompareResult.NoMatch:
|
||||
continue
|
||||
case CompareResult.Includes:
|
||||
res.push(x)
|
||||
break
|
||||
case CompareResult.Equals:
|
||||
res.unshift(x)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}, [sortedItem, search])
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ListContainer className={scrollbar}>
|
||||
<div style={{ margin: "10px 10px", display: "flex" }}>
|
||||
<LockButton onClick={onLock} title={t.action_lock}>
|
||||
<LockButton onClick={onLock} title={t.action.lock}>
|
||||
<FiLock />
|
||||
</LockButton>
|
||||
<SearchContainer>
|
||||
@ -147,8 +158,20 @@ async function arrayFrom<T>(generator: AsyncGenerator<T, void, unknown>) {
|
||||
return list
|
||||
}
|
||||
|
||||
function stringCompare(search: string, source?: string) {
|
||||
if (!search) return true
|
||||
if (!source) return false
|
||||
return source.toLocaleLowerCase().includes(search.toLocaleLowerCase())
|
||||
enum CompareResult {
|
||||
NoMatch,
|
||||
Includes,
|
||||
Equals,
|
||||
}
|
||||
|
||||
function stringCompare(search: string, source?: string) {
|
||||
if (!search) return CompareResult.Includes
|
||||
if (!source) return CompareResult.NoMatch
|
||||
source = source.toLocaleLowerCase()
|
||||
search = search.toLocaleUpperCase()
|
||||
const includes = source.includes(search.toLocaleLowerCase())
|
||||
if (includes) {
|
||||
return source.length === search.length ? CompareResult.Equals : CompareResult.Includes
|
||||
}
|
||||
return CompareResult.NoMatch
|
||||
}
|
||||
|
@ -87,8 +87,8 @@ const PickOPVault: React.FC<{
|
||||
|
||||
return (
|
||||
<PickOPVaultContainer>
|
||||
<button onClick={onClick}>{t.label_choose_a_vault}</button>
|
||||
<PickOPVaultInfo>{t.label_no_vault_selected}</PickOPVaultInfo>
|
||||
<button onClick={onClick}>{t.label.choose_a_vault}</button>
|
||||
<PickOPVaultInfo>{t.label.no_vault_selected}</PickOPVaultInfo>
|
||||
</PickOPVaultContainer>
|
||||
)
|
||||
}
|
||||
|
11
pnpm-lock.yaml
generated
11
pnpm-lock.yaml
generated
@ -35,6 +35,7 @@ importers:
|
||||
sinon-chai: ^3.7.0
|
||||
ts-node: ^10.2.1
|
||||
tsconfig-paths: ^3.11.0
|
||||
tslib: ^2.3.1
|
||||
typescript: ^4.4.3
|
||||
devDependencies:
|
||||
'@types/chai': 4.2.22
|
||||
@ -68,8 +69,15 @@ importers:
|
||||
sinon-chai: 3.7.0_chai@4.3.4+sinon@11.1.2
|
||||
ts-node: 10.2.1_8304ecd715830f7c190b4d1dea90b100
|
||||
tsconfig-paths: 3.11.0
|
||||
tslib: 2.3.1
|
||||
typescript: 4.4.3
|
||||
|
||||
packages/adapters:
|
||||
specifiers:
|
||||
opvault.js: '*'
|
||||
dependencies:
|
||||
opvault.js: link:../opvault.js
|
||||
|
||||
packages/opvault.js:
|
||||
specifiers:
|
||||
'@rollup/plugin-json': ^4.1.0
|
||||
@ -137,6 +145,9 @@ importers:
|
||||
typescript: 4.4.3
|
||||
vite: 2.6.13_sass@1.43.4
|
||||
|
||||
packages/web/dist:
|
||||
specifiers: {}
|
||||
|
||||
packages:
|
||||
|
||||
/7zip-bin/5.1.1:
|
||||
|
@ -2,8 +2,8 @@ import { resolve } from "path";
|
||||
import { describe, it, beforeEach } from "mocha";
|
||||
import { expect } from "chai";
|
||||
|
||||
import type { Vault } from "../packages/opvault.js/index";
|
||||
import { OnePassword } from "../packages/opvault.js/index";
|
||||
import type { Vault } from "../packages/opvault.js/src/index";
|
||||
import { OnePassword } from "../packages/opvault.js/src/index";
|
||||
|
||||
describe("OnePassword", () => {
|
||||
const freddy = resolve(__dirname, "../freddy-2013-12-04.opvault");
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { describe, it } from "mocha";
|
||||
import { expect } from "chai";
|
||||
|
||||
import { WeakValueMap } from "../packages/opvault.js/weakMap";
|
||||
import { WeakValueMap } from "../packages/opvault.js/src/weakMap";
|
||||
|
||||
declare const gc: () => void;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user