Merge branch 'notification-read-api' into swn
This commit is contained in:
commit
ea9f0be129
26 changed files with 688 additions and 618 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -10,6 +10,17 @@
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## 12.x.x (unreleased)
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
- アカウント削除に確認ダイアログを出すように
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
## 12.96.1 (2021/11/13)
|
||||||
|
### Improvements
|
||||||
|
- npm scriptの互換性を向上
|
||||||
|
|
||||||
## 12.96.0 (2021/11/13)
|
## 12.96.0 (2021/11/13)
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
|
|
|
@ -29,7 +29,6 @@ COPY --from=builder /misskey/built ./built
|
||||||
COPY --from=builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
|
COPY --from=builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
|
||||||
COPY --from=builder /misskey/packages/backend/built ./packages/backend/built
|
COPY --from=builder /misskey/packages/backend/built ./packages/backend/built
|
||||||
COPY --from=builder /misskey/packages/client/node_modules ./packages/client/node_modules
|
COPY --from=builder /misskey/packages/client/node_modules ./packages/client/node_modules
|
||||||
COPY --from=builder /misskey/packages/client/built ./packages/client/built
|
|
||||||
COPY . ./
|
COPY . ./
|
||||||
|
|
||||||
CMD ["npm", "run", "migrateandstart"]
|
CMD ["npm", "run", "migrateandstart"]
|
||||||
|
|
|
@ -806,6 +806,7 @@ muteThread: "スレッドをミュート"
|
||||||
unmuteThread: "スレッドのミュートを解除"
|
unmuteThread: "スレッドのミュートを解除"
|
||||||
ffVisibility: "つながりの公開範囲"
|
ffVisibility: "つながりの公開範囲"
|
||||||
ffVisibilityDescription: "自分のフォロー/フォロワー情報の公開範囲を設定できます。"
|
ffVisibilityDescription: "自分のフォロー/フォロワー情報の公開範囲を設定できます。"
|
||||||
|
deleteAccountConfirm: "アカウントが削除されます。よろしいですか?"
|
||||||
|
|
||||||
_emailUnavailable:
|
_emailUnavailable:
|
||||||
used: "既に使用されています"
|
used: "既に使用されています"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "12.96.0",
|
"version": "12.96.1",
|
||||||
"codename": "indigo",
|
"codename": "indigo",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -29,7 +29,8 @@
|
||||||
"test": "npm run mocha",
|
"test": "npm run mocha",
|
||||||
"format": "gulp format",
|
"format": "gulp format",
|
||||||
"clean": "node ./scripts/clean.js",
|
"clean": "node ./scripts/clean.js",
|
||||||
"clean-all": "node ./scripts/clean-all.js"
|
"clean-all": "node ./scripts/clean-all.js",
|
||||||
|
"cleanall": "npm run clean-all"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/gulp": "4.0.9",
|
"@types/gulp": "4.0.9",
|
||||||
|
@ -39,7 +40,8 @@
|
||||||
"gulp-cssnano": "2.1.3",
|
"gulp-cssnano": "2.1.3",
|
||||||
"gulp-rename": "2.0.0",
|
"gulp-rename": "2.0.0",
|
||||||
"gulp-replace": "1.1.3",
|
"gulp-replace": "1.1.3",
|
||||||
"gulp-terser": "2.1.0"
|
"gulp-terser": "2.1.0",
|
||||||
|
"js-yaml": "4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@redocly/openapi-core": "1.0.0-beta.54",
|
"@redocly/openapi-core": "1.0.0-beta.54",
|
||||||
|
|
|
@ -31,6 +31,10 @@ module.exports = {
|
||||||
'before': true,
|
'before': true,
|
||||||
'after': true,
|
'after': true,
|
||||||
}],
|
}],
|
||||||
|
'key-spacing': ['error', {
|
||||||
|
'beforeColon': false,
|
||||||
|
'afterColon': true,
|
||||||
|
}],
|
||||||
/* TODO: path aliasを使わないとwarnする
|
/* TODO: path aliasを使わないとwarnする
|
||||||
'no-restricted-imports': ['warn', {
|
'no-restricted-imports': ['warn', {
|
||||||
'patterns': [
|
'patterns': [
|
||||||
|
@ -47,10 +51,15 @@ module.exports = {
|
||||||
'no-async-promise-executor': ['off'],
|
'no-async-promise-executor': ['off'],
|
||||||
'no-useless-escape': ['off'],
|
'no-useless-escape': ['off'],
|
||||||
'no-multi-spaces': ['warn'],
|
'no-multi-spaces': ['warn'],
|
||||||
|
'no-multiple-empty-lines': ['error', { 'max': 1 }],
|
||||||
'no-control-regex': ['warn'],
|
'no-control-regex': ['warn'],
|
||||||
'no-empty': ['warn'],
|
'no-empty': ['warn'],
|
||||||
'no-inner-declarations': ['off'],
|
'no-inner-declarations': ['off'],
|
||||||
'no-sparse-arrays': ['off'],
|
'no-sparse-arrays': ['off'],
|
||||||
|
'nonblock-statement-body-position': ['error', 'beside'],
|
||||||
|
'object-curly-spacing': ['error', 'always'],
|
||||||
|
'space-infix-ops': ['error'],
|
||||||
|
'space-before-blocks': ['error', 'always'],
|
||||||
'@typescript-eslint/no-var-requires': ['warn'],
|
'@typescript-eslint/no-var-requires': ['warn'],
|
||||||
'@typescript-eslint/no-inferrable-types': ['warn'],
|
'@typescript-eslint/no-inferrable-types': ['warn'],
|
||||||
'@typescript-eslint/no-empty-function': ['off'],
|
'@typescript-eslint/no-empty-function': ['off'],
|
||||||
|
|
|
@ -82,8 +82,7 @@ export default class Reversi {
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
// ゲームが始まった時点で片方の色の石しかないか、始まった時点で勝敗が決定するようなマップの場合がある
|
// ゲームが始まった時点で片方の色の石しかないか、始まった時点で勝敗が決定するようなマップの場合がある
|
||||||
if (!this.canPutSomewhere(BLACK))
|
if (!this.canPutSomewhere(BLACK)) this.turn = this.canPutSomewhere(WHITE) ? WHITE : null;
|
||||||
this.turn = this.canPutSomewhere(WHITE) ? WHITE : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -226,11 +225,12 @@ export default class Reversi {
|
||||||
// 座標が指し示す位置がボード外に出たとき
|
// 座標が指し示す位置がボード外に出たとき
|
||||||
if (this.opts.loopedBoard && this.transformXyToPos(
|
if (this.opts.loopedBoard && this.transformXyToPos(
|
||||||
(x = ((x % this.mapWidth) + this.mapWidth) % this.mapWidth),
|
(x = ((x % this.mapWidth) + this.mapWidth) % this.mapWidth),
|
||||||
(y = ((y % this.mapHeight) + this.mapHeight) % this.mapHeight)) === initPos)
|
(y = ((y % this.mapHeight) + this.mapHeight) % this.mapHeight)) === initPos) {
|
||||||
// 盤面の境界でループし、自分が石を置く位置に戻ってきたとき、挟めるようにしている (ref: Test4のマップ)
|
// 盤面の境界でループし、自分が石を置く位置に戻ってきたとき、挟めるようにしている (ref: Test4のマップ)
|
||||||
return found;
|
return found;
|
||||||
else if (x === -1 || y === -1 || x === this.mapWidth || y === this.mapHeight)
|
} else if (x === -1 || y === -1 || x === this.mapWidth || y === this.mapHeight) {
|
||||||
return []; // 挟めないことが確定 (盤面外に到達)
|
return []; // 挟めないことが確定 (盤面外に到達)
|
||||||
|
}
|
||||||
|
|
||||||
const pos = this.transformXyToPos(x, y);
|
const pos = this.transformXyToPos(x, y);
|
||||||
if (this.mapDataGet(pos) === 'null') return []; // 挟めないことが確定 (配置不可能なマスに到達)
|
if (this.mapDataGet(pos) === 'null') return []; // 挟めないことが確定 (配置不可能なマスに到達)
|
||||||
|
|
|
@ -54,7 +54,7 @@ export async function populateEmoji(emojiName: string, noteUserHost: string | nu
|
||||||
|
|
||||||
const queryOrNull = async () => (await Emojis.findOne({
|
const queryOrNull = async () => (await Emojis.findOne({
|
||||||
name,
|
name,
|
||||||
host
|
host,
|
||||||
})) || null;
|
})) || null;
|
||||||
|
|
||||||
const emoji = await cache.fetch(`${name} ${host}`, queryOrNull);
|
const emoji = await cache.fetch(`${name} ${host}`, queryOrNull);
|
||||||
|
@ -111,12 +111,12 @@ export async function prefetchEmojis(emojis: { name: string; host: string | null
|
||||||
for (const host of hosts) {
|
for (const host of hosts) {
|
||||||
emojisQuery.push({
|
emojisQuery.push({
|
||||||
name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)),
|
name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)),
|
||||||
host: host
|
host: host,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const _emojis = emojisQuery.length > 0 ? await Emojis.find({
|
const _emojis = emojisQuery.length > 0 ? await Emojis.find({
|
||||||
where: emojisQuery,
|
where: emojisQuery,
|
||||||
select: ['name', 'host', 'url']
|
select: ['name', 'host', 'url'],
|
||||||
}) : [];
|
}) : [];
|
||||||
for (const emoji of _emojis) {
|
for (const emoji of _emojis) {
|
||||||
cache.set(`${emoji.name} ${emoji.host}`, emoji);
|
cache.set(`${emoji.name} ${emoji.host}`, emoji);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable key-spacing */
|
||||||
import { emojiRegex } from './emoji-regex';
|
import { emojiRegex } from './emoji-regex';
|
||||||
import { fetchMeta } from './fetch-meta';
|
import { fetchMeta } from './fetch-meta';
|
||||||
import { Emojis } from '@/models/index';
|
import { Emojis } from '@/models/index';
|
||||||
|
|
|
@ -66,7 +66,7 @@ function signToRequest(request: Request, key: PrivateKey, includeHeaders: string
|
||||||
const signatureHeader = `keyId="${key.keyId}",algorithm="rsa-sha256",headers="${includeHeaders.join(' ')}",signature="${signature}"`;
|
const signatureHeader = `keyId="${key.keyId}",algorithm="rsa-sha256",headers="${includeHeaders.join(' ')}",signature="${signature}"`;
|
||||||
|
|
||||||
request.headers = objectAssignWithLcKey(request.headers, {
|
request.headers = objectAssignWithLcKey(request.headers, {
|
||||||
Signature: signatureHeader
|
Signature: signatureHeader,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -163,7 +163,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
||||||
uri: person.id,
|
uri: person.id,
|
||||||
tags,
|
tags,
|
||||||
isBot,
|
isBot,
|
||||||
isCat: (person as any).isCat === true
|
isCat: (person as any).isCat === true,
|
||||||
})) as IRemoteUser;
|
})) as IRemoteUser;
|
||||||
|
|
||||||
await transactionalEntityManager.save(new UserProfile({
|
await transactionalEntityManager.save(new UserProfile({
|
||||||
|
@ -173,14 +173,14 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
||||||
fields,
|
fields,
|
||||||
birthday: bday ? bday[0] : null,
|
birthday: bday ? bday[0] : null,
|
||||||
location: person['vcard:Address'] || null,
|
location: person['vcard:Address'] || null,
|
||||||
userHost: host
|
userHost: host,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (person.publicKey) {
|
if (person.publicKey) {
|
||||||
await transactionalEntityManager.save(new UserPublickey({
|
await transactionalEntityManager.save(new UserPublickey({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
keyId: person.publicKey.id,
|
keyId: person.publicKey.id,
|
||||||
keyPem: person.publicKey.publicKeyPem
|
keyPem: person.publicKey.publicKeyPem,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -189,7 +189,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
||||||
if (isDuplicateKeyValueError(e)) {
|
if (isDuplicateKeyValueError(e)) {
|
||||||
// /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応
|
// /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応
|
||||||
const u = await Users.findOne({
|
const u = await Users.findOne({
|
||||||
uri: person.id
|
uri: person.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (u) {
|
if (u) {
|
||||||
|
@ -218,11 +218,11 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
||||||
//#region アバターとヘッダー画像をフェッチ
|
//#region アバターとヘッダー画像をフェッチ
|
||||||
const [avatar, banner] = await Promise.all([
|
const [avatar, banner] = await Promise.all([
|
||||||
person.icon,
|
person.icon,
|
||||||
person.image
|
person.image,
|
||||||
].map(img =>
|
].map(img =>
|
||||||
img == null
|
img == null
|
||||||
? Promise.resolve(null)
|
? Promise.resolve(null)
|
||||||
: resolveImage(user!, img).catch(() => null)
|
: resolveImage(user!, img).catch(() => null),
|
||||||
));
|
));
|
||||||
|
|
||||||
const avatarId = avatar ? avatar.id : null;
|
const avatarId = avatar ? avatar.id : null;
|
||||||
|
@ -258,7 +258,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
||||||
const emojiNames = emojis.map(emoji => emoji.name);
|
const emojiNames = emojis.map(emoji => emoji.name);
|
||||||
|
|
||||||
await Users.update(user!.id, {
|
await Users.update(user!.id, {
|
||||||
emojis: emojiNames
|
emojis: emojiNames,
|
||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -301,11 +301,11 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
|
||||||
// アバターとヘッダー画像をフェッチ
|
// アバターとヘッダー画像をフェッチ
|
||||||
const [avatar, banner] = await Promise.all([
|
const [avatar, banner] = await Promise.all([
|
||||||
person.icon,
|
person.icon,
|
||||||
person.image
|
person.image,
|
||||||
].map(img =>
|
].map(img =>
|
||||||
img == null
|
img == null
|
||||||
? Promise.resolve(null)
|
? Promise.resolve(null)
|
||||||
: resolveImage(exist, img).catch(() => null)
|
: resolveImage(exist, img).catch(() => null),
|
||||||
));
|
));
|
||||||
|
|
||||||
// カスタム絵文字取得
|
// カスタム絵文字取得
|
||||||
|
@ -355,7 +355,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
|
||||||
if (person.publicKey) {
|
if (person.publicKey) {
|
||||||
await UserPublickeys.update({ userId: exist.id }, {
|
await UserPublickeys.update({ userId: exist.id }, {
|
||||||
keyId: person.publicKey.id,
|
keyId: person.publicKey.id,
|
||||||
keyPem: person.publicKey.publicKeyPem
|
keyPem: person.publicKey.publicKeyPem,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,9 +372,9 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
|
||||||
|
|
||||||
// 該当ユーザーが既にフォロワーになっていた場合はFollowingもアップデートする
|
// 該当ユーザーが既にフォロワーになっていた場合はFollowingもアップデートする
|
||||||
await Followings.update({
|
await Followings.update({
|
||||||
followerId: exist.id
|
followerId: exist.id,
|
||||||
}, {
|
}, {
|
||||||
followerSharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined)
|
followerSharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined),
|
||||||
});
|
});
|
||||||
|
|
||||||
await updateFeatured(exist.id).catch(err => logger.error(err));
|
await updateFeatured(exist.id).catch(err => logger.error(err));
|
||||||
|
@ -411,8 +411,9 @@ const services: {
|
||||||
};
|
};
|
||||||
|
|
||||||
const $discord = (id: string, name: string) => {
|
const $discord = (id: string, name: string) => {
|
||||||
if (typeof name !== 'string')
|
if (typeof name !== 'string') {
|
||||||
name = 'unknown#0000';
|
name = 'unknown#0000';
|
||||||
|
}
|
||||||
const [username, discriminator] = name.split('#');
|
const [username, discriminator] = name.split('#');
|
||||||
return { id, username, discriminator };
|
return { id, username, discriminator };
|
||||||
};
|
};
|
||||||
|
@ -420,14 +421,16 @@ const $discord = (id: string, name: string) => {
|
||||||
function addService(target: { [x: string]: any }, source: IApPropertyValue) {
|
function addService(target: { [x: string]: any }, source: IApPropertyValue) {
|
||||||
const service = services[source.name];
|
const service = services[source.name];
|
||||||
|
|
||||||
if (typeof source.value !== 'string')
|
if (typeof source.value !== 'string') {
|
||||||
source.value = 'unknown';
|
source.value = 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
const [id, username] = source.value.split('@');
|
const [id, username] = source.value.split('@');
|
||||||
|
|
||||||
if (service)
|
if (service) {
|
||||||
target[source.name.split(':')[2]] = service(id, username);
|
target[source.name.split(':')[2]] = service(id, username);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function analyzeAttachments(attachments: IObject | IObject[] | undefined) {
|
export function analyzeAttachments(attachments: IObject | IObject[] | undefined) {
|
||||||
const fields: {
|
const fields: {
|
||||||
|
@ -443,7 +446,7 @@ export function analyzeAttachments(attachments: IObject | IObject[] | undefined)
|
||||||
} else {
|
} else {
|
||||||
fields.push({
|
fields.push({
|
||||||
name: attachment.name,
|
name: attachment.name,
|
||||||
value: fromHtml(attachment.value)
|
value: fromHtml(attachment.value),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -487,7 +490,7 @@ export async function updateFeatured(userId: User['id']) {
|
||||||
id: genId(new Date(Date.now() + td)),
|
id: genId(new Date(Date.now() + td)),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
noteId: note!.id
|
noteId: note!.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,7 +6,7 @@ const ECC_PRELUDE = Buffer.from([0x04]);
|
||||||
const NULL_BYTE = Buffer.from([0]);
|
const NULL_BYTE = Buffer.from([0]);
|
||||||
const PEM_PRELUDE = Buffer.from(
|
const PEM_PRELUDE = Buffer.from(
|
||||||
'3059301306072a8648ce3d020106082a8648ce3d030107034200',
|
'3059301306072a8648ce3d020106082a8648ce3d030107034200',
|
||||||
'hex'
|
'hex',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Android Safetynet attestations are signed with this cert:
|
// Android Safetynet attestations are signed with this cert:
|
||||||
|
@ -134,7 +134,7 @@ export function verifyLogin({
|
||||||
|
|
||||||
const verificationData = Buffer.concat(
|
const verificationData = Buffer.concat(
|
||||||
[authenticatorData, hash(clientDataJSON)],
|
[authenticatorData, hash(clientDataJSON)],
|
||||||
32 + authenticatorData.length
|
32 + authenticatorData.length,
|
||||||
);
|
);
|
||||||
|
|
||||||
return crypto
|
return crypto
|
||||||
|
@ -158,14 +158,14 @@ export const procedures = {
|
||||||
|
|
||||||
const publicKeyU2F = Buffer.concat(
|
const publicKeyU2F = Buffer.concat(
|
||||||
[ECC_PRELUDE, negTwo, negThree],
|
[ECC_PRELUDE, negTwo, negThree],
|
||||||
1 + 32 + 32
|
1 + 32 + 32,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
publicKey: publicKeyU2F,
|
publicKey: publicKeyU2F,
|
||||||
valid: true
|
valid: true,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
'android-key': {
|
'android-key': {
|
||||||
verify({
|
verify({
|
||||||
|
@ -174,7 +174,7 @@ export const procedures = {
|
||||||
clientDataHash,
|
clientDataHash,
|
||||||
publicKey,
|
publicKey,
|
||||||
rpIdHash,
|
rpIdHash,
|
||||||
credentialId
|
credentialId,
|
||||||
}: {
|
}: {
|
||||||
attStmt: any,
|
attStmt: any,
|
||||||
authenticatorData: Buffer,
|
authenticatorData: Buffer,
|
||||||
|
@ -189,7 +189,7 @@ export const procedures = {
|
||||||
|
|
||||||
const verificationData = Buffer.concat([
|
const verificationData = Buffer.concat([
|
||||||
authenticatorData,
|
authenticatorData,
|
||||||
clientDataHash
|
clientDataHash,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const attCert: Buffer = attStmt.x5c[0];
|
const attCert: Buffer = attStmt.x5c[0];
|
||||||
|
@ -206,7 +206,7 @@ export const procedures = {
|
||||||
|
|
||||||
const publicKeyData = Buffer.concat(
|
const publicKeyData = Buffer.concat(
|
||||||
[ECC_PRELUDE, negTwo, negThree],
|
[ECC_PRELUDE, negTwo, negThree],
|
||||||
1 + 32 + 32
|
1 + 32 + 32,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!attCert.equals(publicKeyData)) {
|
if (!attCert.equals(publicKeyData)) {
|
||||||
|
@ -222,9 +222,9 @@ export const procedures = {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
valid: isValid,
|
valid: isValid,
|
||||||
publicKey: publicKeyData
|
publicKey: publicKeyData,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
// what a stupid attestation
|
// what a stupid attestation
|
||||||
'android-safetynet': {
|
'android-safetynet': {
|
||||||
|
@ -234,7 +234,7 @@ export const procedures = {
|
||||||
clientDataHash,
|
clientDataHash,
|
||||||
publicKey,
|
publicKey,
|
||||||
rpIdHash,
|
rpIdHash,
|
||||||
credentialId
|
credentialId,
|
||||||
}: {
|
}: {
|
||||||
attStmt: any,
|
attStmt: any,
|
||||||
authenticatorData: Buffer,
|
authenticatorData: Buffer,
|
||||||
|
@ -244,14 +244,14 @@ export const procedures = {
|
||||||
credentialId: Buffer,
|
credentialId: Buffer,
|
||||||
}) {
|
}) {
|
||||||
const verificationData = hash(
|
const verificationData = hash(
|
||||||
Buffer.concat([authenticatorData, clientDataHash])
|
Buffer.concat([authenticatorData, clientDataHash]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const jwsParts = attStmt.response.toString('utf-8').split('.');
|
const jwsParts = attStmt.response.toString('utf-8').split('.');
|
||||||
|
|
||||||
const header = JSON.parse(base64URLDecode(jwsParts[0]).toString('utf-8'));
|
const header = JSON.parse(base64URLDecode(jwsParts[0]).toString('utf-8'));
|
||||||
const response = JSON.parse(
|
const response = JSON.parse(
|
||||||
base64URLDecode(jwsParts[1]).toString('utf-8')
|
base64URLDecode(jwsParts[1]).toString('utf-8'),
|
||||||
);
|
);
|
||||||
const signature = jwsParts[2];
|
const signature = jwsParts[2];
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ export const procedures = {
|
||||||
|
|
||||||
const signatureBase = Buffer.from(
|
const signatureBase = Buffer.from(
|
||||||
jwsParts[0] + '.' + jwsParts[1],
|
jwsParts[0] + '.' + jwsParts[1],
|
||||||
'utf-8'
|
'utf-8',
|
||||||
);
|
);
|
||||||
|
|
||||||
const valid = crypto
|
const valid = crypto
|
||||||
|
@ -293,13 +293,13 @@ export const procedures = {
|
||||||
|
|
||||||
const publicKeyData = Buffer.concat(
|
const publicKeyData = Buffer.concat(
|
||||||
[ECC_PRELUDE, negTwo, negThree],
|
[ECC_PRELUDE, negTwo, negThree],
|
||||||
1 + 32 + 32
|
1 + 32 + 32,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
valid,
|
valid,
|
||||||
publicKey: publicKeyData
|
publicKey: publicKeyData,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
packed: {
|
packed: {
|
||||||
verify({
|
verify({
|
||||||
|
@ -308,7 +308,7 @@ export const procedures = {
|
||||||
clientDataHash,
|
clientDataHash,
|
||||||
publicKey,
|
publicKey,
|
||||||
rpIdHash,
|
rpIdHash,
|
||||||
credentialId
|
credentialId,
|
||||||
}: {
|
}: {
|
||||||
attStmt: any,
|
attStmt: any,
|
||||||
authenticatorData: Buffer,
|
authenticatorData: Buffer,
|
||||||
|
@ -319,7 +319,7 @@ export const procedures = {
|
||||||
}) {
|
}) {
|
||||||
const verificationData = Buffer.concat([
|
const verificationData = Buffer.concat([
|
||||||
authenticatorData,
|
authenticatorData,
|
||||||
clientDataHash
|
clientDataHash,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (attStmt.x5c) {
|
if (attStmt.x5c) {
|
||||||
|
@ -342,12 +342,12 @@ export const procedures = {
|
||||||
|
|
||||||
const publicKeyData = Buffer.concat(
|
const publicKeyData = Buffer.concat(
|
||||||
[ECC_PRELUDE, negTwo, negThree],
|
[ECC_PRELUDE, negTwo, negThree],
|
||||||
1 + 32 + 32
|
1 + 32 + 32,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
valid: validSignature,
|
valid: validSignature,
|
||||||
publicKey: publicKeyData
|
publicKey: publicKeyData,
|
||||||
};
|
};
|
||||||
} else if (attStmt.ecdaaKeyId) {
|
} else if (attStmt.ecdaaKeyId) {
|
||||||
// https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-ecdaa-algorithm-v2.0-id-20180227.html#ecdaa-verify-operation
|
// https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-ecdaa-algorithm-v2.0-id-20180227.html#ecdaa-verify-operation
|
||||||
|
@ -357,7 +357,7 @@ export const procedures = {
|
||||||
|
|
||||||
throw new Error('self attestation is not supported');
|
throw new Error('self attestation is not supported');
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
'fido-u2f': {
|
'fido-u2f': {
|
||||||
|
@ -367,7 +367,7 @@ export const procedures = {
|
||||||
clientDataHash,
|
clientDataHash,
|
||||||
publicKey,
|
publicKey,
|
||||||
rpIdHash,
|
rpIdHash,
|
||||||
credentialId
|
credentialId,
|
||||||
}: {
|
}: {
|
||||||
attStmt: any,
|
attStmt: any,
|
||||||
authenticatorData: Buffer,
|
authenticatorData: Buffer,
|
||||||
|
@ -397,7 +397,7 @@ export const procedures = {
|
||||||
|
|
||||||
const publicKeyU2F = Buffer.concat(
|
const publicKeyU2F = Buffer.concat(
|
||||||
[ECC_PRELUDE, negTwo, negThree],
|
[ECC_PRELUDE, negTwo, negThree],
|
||||||
1 + 32 + 32
|
1 + 32 + 32,
|
||||||
);
|
);
|
||||||
|
|
||||||
const verificationData = Buffer.concat([
|
const verificationData = Buffer.concat([
|
||||||
|
@ -405,7 +405,7 @@ export const procedures = {
|
||||||
rpIdHash,
|
rpIdHash,
|
||||||
clientDataHash,
|
clientDataHash,
|
||||||
credentialId,
|
credentialId,
|
||||||
publicKeyU2F
|
publicKeyU2F,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const validSignature = crypto
|
const validSignature = crypto
|
||||||
|
@ -415,8 +415,8 @@ export const procedures = {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
valid: validSignature,
|
valid: validSignature,
|
||||||
publicKey: publicKeyU2F
|
publicKey: publicKeyU2F,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -244,8 +244,9 @@ export default define(meta, async (ps, user) => {
|
||||||
|
|
||||||
if (ps.poll) {
|
if (ps.poll) {
|
||||||
if (typeof ps.poll.expiresAt === 'number') {
|
if (typeof ps.poll.expiresAt === 'number') {
|
||||||
if (ps.poll.expiresAt < Date.now())
|
if (ps.poll.expiresAt < Date.now()) {
|
||||||
throw new ApiError(meta.errors.cannotCreateAlreadyExpiredPoll);
|
throw new ApiError(meta.errors.cannotCreateAlreadyExpiredPoll);
|
||||||
|
}
|
||||||
} else if (typeof ps.poll.expiredAfter === 'number') {
|
} else if (typeof ps.poll.expiredAfter === 'number') {
|
||||||
ps.poll.expiresAt = Date.now() + ps.poll.expiredAfter;
|
ps.poll.expiresAt = Date.now() + ps.poll.expiredAfter;
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,8 +112,9 @@ export default define(meta, async (ps, user) => {
|
||||||
|
|
||||||
if (exist.length) {
|
if (exist.length) {
|
||||||
if (poll.multiple) {
|
if (poll.multiple) {
|
||||||
if (exist.some(x => x.choice == ps.choice))
|
if (exist.some(x => x.choice == ps.choice)) {
|
||||||
throw new ApiError(meta.errors.alreadyVoted);
|
throw new ApiError(meta.errors.alreadyVoted);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new ApiError(meta.errors.alreadyVoted);
|
throw new ApiError(meta.errors.alreadyVoted);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ router.get('/disconnect/github', async ctx => {
|
||||||
|
|
||||||
const user = await Users.findOneOrFail({
|
const user = await Users.findOneOrFail({
|
||||||
host: null,
|
host: null,
|
||||||
token: userToken
|
token: userToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
const profile = await UserProfiles.findOneOrFail(user.id);
|
const profile = await UserProfiles.findOneOrFail(user.id);
|
||||||
|
@ -58,7 +58,7 @@ router.get('/disconnect/github', async ctx => {
|
||||||
// Publish i updated event
|
// Publish i updated event
|
||||||
publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, {
|
publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, {
|
||||||
detail: true,
|
detail: true,
|
||||||
includeSecrets: true
|
includeSecrets: true,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -209,12 +209,13 @@ router.get('/gh/cb', async ctx => {
|
||||||
code,
|
code,
|
||||||
{ redirect_uri },
|
{ redirect_uri },
|
||||||
(err, accessToken, refresh, result) => {
|
(err, accessToken, refresh, result) => {
|
||||||
if (err)
|
if (err) {
|
||||||
rej(err);
|
rej(err);
|
||||||
else if (result.error)
|
} else if (result.error) {
|
||||||
rej(result.error);
|
rej(result.error);
|
||||||
else
|
} else {
|
||||||
res({ accessToken });
|
res({ accessToken });
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { login, id } = await getJson('https://api.github.com/user', 'application/vnd.github.v3+json', 10 * 1000, {
|
const { login, id } = await getJson('https://api.github.com/user', 'application/vnd.github.v3+json', 10 * 1000, {
|
||||||
|
|
|
@ -45,8 +45,6 @@
|
||||||
localStorage.setItem('lang', lang);
|
localStorage.setItem('lang', lang);
|
||||||
localStorage.setItem('locale', await res.text());
|
localStorage.setItem('locale', await res.text());
|
||||||
localStorage.setItem('localeVersion', v);
|
localStorage.setItem('localeVersion', v);
|
||||||
} else if (localeOutdated) {
|
|
||||||
// nop
|
|
||||||
} else {
|
} else {
|
||||||
await checkUpdate();
|
await checkUpdate();
|
||||||
renderError('LOCALE_FETCH_FAILED');
|
renderError('LOCALE_FETCH_FAILED');
|
||||||
|
|
|
@ -41,7 +41,7 @@ router.get('/.well-known/host-meta', async ctx => {
|
||||||
ctx.set('Content-Type', xrd);
|
ctx.set('Content-Type', xrd);
|
||||||
ctx.body = XRD({ element: 'Link', attributes: {
|
ctx.body = XRD({ element: 'Link', attributes: {
|
||||||
type: xrd,
|
type: xrd,
|
||||||
template: `${config.url}${webFingerPath}?resource={uri}`
|
template: `${config.url}${webFingerPath}?resource={uri}`,
|
||||||
} });
|
} });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -51,8 +51,8 @@ router.get('/.well-known/host-meta.json', async ctx => {
|
||||||
links: [{
|
links: [{
|
||||||
rel: 'lrdd',
|
rel: 'lrdd',
|
||||||
type: jrd,
|
type: jrd,
|
||||||
template: `${config.url}${webFingerPath}?resource={uri}`
|
template: `${config.url}${webFingerPath}?resource={uri}`,
|
||||||
}]
|
}],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,7 @@
|
||||||
"punycode": "2.1.1",
|
"punycode": "2.1.1",
|
||||||
"pureimage": "0.3.5",
|
"pureimage": "0.3.5",
|
||||||
"qrcode": "1.4.4",
|
"qrcode": "1.4.4",
|
||||||
|
"querystring": "0.2.1",
|
||||||
"random-seed": "0.3.0",
|
"random-seed": "0.3.0",
|
||||||
"ratelimiter": "3.4.1",
|
"ratelimiter": "3.4.1",
|
||||||
"reflect-metadata": "0.1.13",
|
"reflect-metadata": "0.1.13",
|
||||||
|
|
|
@ -6,21 +6,11 @@
|
||||||
<div class="name">{{ reaction.replace('@.', '') }}</div>
|
<div class="name">{{ reaction.replace('@.', '') }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="users">
|
<div class="users">
|
||||||
<template v-if="users.length <= 10">
|
<div class="user" v-for="u in users" :key="u.id">
|
||||||
<b v-for="u in users" :key="u.id" style="margin-right: 12px;">
|
<MkAvatar class="avatar" :user="u"/>
|
||||||
<MkAvatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
|
<MkUserName class="name" :user="u" :nowrap="true"/>
|
||||||
<br/>
|
</div>
|
||||||
<MkUserName :user="u" :nowrap="false" style="line-height: 24px;"/>
|
<div v-if="users.length > 10" class="omitted">+{{ count - 10 }}</div>
|
||||||
</b>
|
|
||||||
</template>
|
|
||||||
<template v-if="10 < users.length">
|
|
||||||
<b v-for="u in users" :key="u.id" style="margin-right: 12px;">
|
|
||||||
<MkAvatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
|
|
||||||
<br/>
|
|
||||||
<MkUserName :user="u" :nowrap="false" style="line-height: 24px;"/>
|
|
||||||
</b>
|
|
||||||
<span slot="omitted">+{{ count - 10 }}</span>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkTooltip>
|
</MkTooltip>
|
||||||
|
@ -81,13 +71,31 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
> .users {
|
> .users {
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
border-left: solid 0.5px var(--divider);
|
border-left: solid 0.5px var(--divider);
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
|
margin-right: 14px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
> .user {
|
||||||
|
line-height: 24px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .avatar {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<MkTooltip :source="source" ref="tooltip" @closed="$emit('closed')" :max-width="340">
|
<MkTooltip :source="source" ref="tooltip" @closed="$emit('closed')" :max-width="250">
|
||||||
<div class="renoteTooltip">
|
<div class="beaffaef">
|
||||||
<b v-for="u in users" :key="u.id">
|
<div class="user" v-for="u in users" :key="u.id">
|
||||||
<MkAvatar :user="u" style="width: 24px; height: 24px;"/><br/>
|
<MkAvatar class="avatar" :user="u"/>
|
||||||
<MkUserName :user="u" :nowrap="false" style="line-height: 24px;"/>
|
<MkUserName class="name" :user="u" :nowrap="true"/>
|
||||||
</b>
|
</div>
|
||||||
<span v-if="users.length < count" slot="omitted">+{{ count - users.length }}</span>
|
<div v-if="users.length < count" class="omitted">+{{ count - users.length }}</div>
|
||||||
</div>
|
</div>
|
||||||
</MkTooltip>
|
</MkTooltip>
|
||||||
</template>
|
</template>
|
||||||
|
@ -36,11 +36,25 @@ export default defineComponent({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.renoteTooltip {
|
.beaffaef {
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
gap: 12px;
|
text-align: left;
|
||||||
|
|
||||||
|
> .user {
|
||||||
|
line-height: 24px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .avatar {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -42,10 +42,6 @@ import { getAccountFromId } from '@/scripts/get-account-from-id';
|
||||||
|
|
||||||
console.info(`Misskey v${version}`);
|
console.info(`Misskey v${version}`);
|
||||||
|
|
||||||
// boot.jsのやつを解除
|
|
||||||
window.onerror = null;
|
|
||||||
window.onunhandledrejection = null;
|
|
||||||
|
|
||||||
if (_DEV_) {
|
if (_DEV_) {
|
||||||
console.warn('Development mode!!!');
|
console.warn('Development mode!!!');
|
||||||
|
|
||||||
|
@ -223,6 +219,10 @@ const rootEl = document.createElement('div');
|
||||||
document.body.appendChild(rootEl);
|
document.body.appendChild(rootEl);
|
||||||
app.mount(rootEl);
|
app.mount(rootEl);
|
||||||
|
|
||||||
|
// boot.jsのやつを解除
|
||||||
|
window.onerror = null;
|
||||||
|
window.onunhandledrejection = null;
|
||||||
|
|
||||||
reactionPicker.init();
|
reactionPicker.init();
|
||||||
|
|
||||||
if (splash) {
|
if (splash) {
|
||||||
|
|
|
@ -45,6 +45,15 @@ export default defineComponent({
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
async deleteAccount() {
|
async deleteAccount() {
|
||||||
|
{
|
||||||
|
const { canceled } = await os.dialog({
|
||||||
|
type: 'warning',
|
||||||
|
text: this.$ts.deleteAccountConfirm,
|
||||||
|
showCancelButton: true
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
}
|
||||||
|
|
||||||
const { canceled, result: password } = await os.dialog({
|
const { canceled, result: password } = await os.dialog({
|
||||||
title: this.$ts.password,
|
title: this.$ts.password,
|
||||||
input: {
|
input: {
|
||||||
|
|
|
@ -5237,6 +5237,11 @@ querystring@0.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
||||||
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
|
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
|
||||||
|
|
||||||
|
querystring@0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd"
|
||||||
|
integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==
|
||||||
|
|
||||||
quick-lru@^5.1.1:
|
quick-lru@^5.1.1:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
|
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
|
||||||
|
|
|
@ -17,12 +17,6 @@ const execa = require('execa');
|
||||||
stderr: process.stderr,
|
stderr: process.stderr,
|
||||||
});
|
});
|
||||||
|
|
||||||
await execa('npm', ['run', 'build'], {
|
|
||||||
cwd: __dirname + '/../packages/client',
|
|
||||||
stdout: process.stdout,
|
|
||||||
stderr: process.stderr,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('build finishing ...');
|
console.log('build finishing ...');
|
||||||
|
|
||||||
await execa('npm', ['run', 'gulp'], {
|
await execa('npm', ['run', 'gulp'], {
|
||||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -323,6 +323,11 @@ argparse@^1.0.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
sprintf-js "~1.0.2"
|
sprintf-js "~1.0.2"
|
||||||
|
|
||||||
|
argparse@^2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
|
||||||
|
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
|
||||||
|
|
||||||
arr-diff@^4.0.0:
|
arr-diff@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
|
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
|
||||||
|
@ -2300,6 +2305,13 @@ js-levenshtein@^1.1.6:
|
||||||
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
|
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
|
||||||
integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
|
integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
|
||||||
|
|
||||||
|
js-yaml@4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||||
|
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
|
||||||
|
dependencies:
|
||||||
|
argparse "^2.0.1"
|
||||||
|
|
||||||
js-yaml@^3.14.1:
|
js-yaml@^3.14.1:
|
||||||
version "3.14.1"
|
version "3.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
|
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
|
||||||
|
|
Loading…
Reference in a new issue