From 56800ef6f0e39ee15c33dc390dedea183826b870 Mon Sep 17 00:00:00 2001 From: takker99 <37929109+takker99@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:06:46 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat(websocket):=20websocket=E3=81=8B?= =?UTF-8?q?=E3=82=89=E8=BF=94=E3=81=95=E3=82=8C=E3=82=8Bcommit=20error?= =?UTF-8?q?=E3=82=92=E9=81=A9=E5=88=87=E3=81=AB=E5=87=A6=E7=90=86=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SocketIOErrorとTimeoutErrorは3秒待ってから再試行 (本家の挙動と同じ) - NotFastForwardErrorは、新しいページ情報をPullして、それを元にpushし直す - DuplicateTitleErrorは、patchの場合別のタイトルにしてpushし直す このあたりの処理を`push()`に全てまとめ、`deletePage()`や`patch()`などはこれを呼び出すだけにした。 objectのpropertiesを増やしたり、戻り値を生やしたりしたが、破壊的変更はしてないはず --- browser/websocket/_codeBlock.ts | 29 ---- browser/websocket/_fetch.ts | 97 ------------ browser/websocket/deletePage.ts | 52 ++----- browser/websocket/makeChanges.ts | 5 +- browser/websocket/patch.ts | 101 ++++--------- browser/websocket/pin.ts | 104 ++++--------- browser/websocket/push.ts | 157 ++++++++++++++++++++ browser/websocket/suggestUnDupTitle.test.ts | 10 ++ browser/websocket/suggestUnDupTitle.ts | 6 + browser/websocket/updateCodeBlock.ts | 85 ++++------- browser/websocket/updateCodeFile.ts | 86 +++++------ deps/testing.ts | 2 +- 12 files changed, 313 insertions(+), 421 deletions(-) delete mode 100644 browser/websocket/_fetch.ts create mode 100644 browser/websocket/push.ts create mode 100644 browser/websocket/suggestUnDupTitle.test.ts create mode 100644 browser/websocket/suggestUnDupTitle.ts diff --git a/browser/websocket/_codeBlock.ts b/browser/websocket/_codeBlock.ts index 845b1b0..8702123 100644 --- a/browser/websocket/_codeBlock.ts +++ b/browser/websocket/_codeBlock.ts @@ -1,8 +1,4 @@ -import { Change, Socket, wrap } from "../../deps/socket.ts"; -import { Page } from "../../deps/scrapbox-rest.ts"; import { TinyCodeBlock } from "../../rest/getCodeBlocks.ts"; -import { getProjectId, getUserId } from "./id.ts"; -import { pushWithRetry } from "./_fetch.ts"; /** コードブロックのタイトル行の情報を保持しておくためのinterface */ export interface CodeTitle { @@ -11,31 +7,6 @@ export interface CodeTitle { indent: number; } -/** コミットを送信する一連の処理 */ -export const applyCommit = async ( - commits: Change[], - page: Page, - projectName: string, - pageTitle: string, - socket: Socket, - userId?: string, -): ReturnType => { - const [projectId, userId_] = await Promise.all([ - getProjectId(projectName), - userId ?? getUserId(), - ]); - const { request } = wrap(socket); - return await pushWithRetry(request, commits, { - parentId: page.commitId, - projectId: projectId, - pageId: page.id, - userId: userId_, - project: projectName, - title: pageTitle, - retry: 3, - }); -}; - /** コードブロックのタイトル行から各種プロパティを抽出する * * @param lineText {string} 行テキスト diff --git a/browser/websocket/_fetch.ts b/browser/websocket/_fetch.ts deleted file mode 100644 index 94f5f49..0000000 --- a/browser/websocket/_fetch.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { - Change, - CommitNotification, - DeletePageChange, - PageCommitError, - PageCommitResponse, - PinChange, - ProjectUpdatesStreamCommit, - ProjectUpdatesStreamEvent, - Result, - TimeoutError, - UnexpectedError, - wrap, -} from "../../deps/socket.ts"; -import { pull } from "./pull.ts"; -export type { - CommitNotification, - ProjectUpdatesStreamCommit, - ProjectUpdatesStreamEvent, -}; - -export type RequestFunc = ReturnType["request"]; -export type PushCommitInit = { - parentId: string; - projectId: string; - pageId: string; - userId: string; -}; - -export const pushCommit = ( - request: RequestFunc, - changes: Change[] | [DeletePageChange] | [PinChange], - commitInit: PushCommitInit, -): Promise< - Result< - PageCommitResponse, - UnexpectedError | TimeoutError | PageCommitError - > -> => - changes.length === 0 - ? Promise.resolve({ ok: true, value: { commitId: commitInit.parentId } }) - : request("socket.io-request", { - method: "commit", - data: { - kind: "page", - ...commitInit, - changes, - cursor: null, - freeze: true, - }, - }) as Promise< - Result< - PageCommitResponse, - UnexpectedError | TimeoutError | PageCommitError - > - >; - -export const pushWithRetry = async ( - request: RequestFunc, - changes: Change[] | [DeletePageChange] | [PinChange], - { project, title, retry = 3, parentId, ...commitInit }: - & PushCommitInit - & { - project: string; - title: string; - retry?: number; - }, -) => { - try { - const res = await pushCommit(request, changes, { - parentId, - ...commitInit, - }); - if (!res.ok) throw Error("Faild to push a commit"); - parentId = res.value.commitId; - } catch (_) { - console.log("Faild to push a commit. Retry after pulling new commits"); - for (let i = 0; i < retry; i++) { - const { commitId } = await pull(project, title); - parentId = commitId; - try { - const res = await pushCommit(request, changes, { - parentId, - ...commitInit, - }); - if (!res.ok) throw Error("Faild to push a commit"); - parentId = res.value.commitId; - console.log("Success in retrying"); - break; - } catch (_) { - continue; - } - } - throw Error("Faild to retry pushing."); - } - return parentId; -}; diff --git a/browser/websocket/deletePage.ts b/browser/websocket/deletePage.ts index 97eb82c..fe2f096 100644 --- a/browser/websocket/deletePage.ts +++ b/browser/websocket/deletePage.ts @@ -1,50 +1,22 @@ -import { Socket, socketIO, wrap } from "../../deps/socket.ts"; -import { connect, disconnect } from "./socket.ts"; -import { getProjectId, getUserId } from "./id.ts"; -import { pull } from "./pull.ts"; -import { pushWithRetry } from "./_fetch.ts"; +import { push, PushOptions, RetryError } from "./push.ts"; +import { Result } from "../../rest/util.ts"; -export interface DeletePageOptions { - socket?: Socket; -} +export type DeletePageOptions = PushOptions; /** 指定したページを削除する * * @param project 削除したいページのproject * @param title 削除したいページのタイトル - * @param options 使用したいSocketがあれば指定する + * @param options */ -export const deletePage = async ( +export const deletePage = ( project: string, title: string, options?: DeletePageOptions, -): Promise => { - const [ - { id: pageId, commitId: parentId, persistent }, - projectId, - userId, - ] = await Promise.all([ - pull(project, title), - getProjectId(project), - getUserId(), - ]); - - if (!persistent) return; - - const injectedSocket = options?.socket; - const socket = injectedSocket ?? await socketIO(); - await connect(socket); - const { request } = wrap(socket); - try { - await pushWithRetry(request, [{ deleted: true }], { - projectId, - pageId, - parentId, - userId, - project, - title, - }); - } finally { - if (!injectedSocket) await disconnect(socket); - } -}; +): Promise> => + push( + project, + title, + (page) => page.persistent ? [{ deleted: true }] : [], + options, + ); diff --git a/browser/websocket/makeChanges.ts b/browser/websocket/makeChanges.ts index 0f1111f..3e6c7cc 100644 --- a/browser/websocket/makeChanges.ts +++ b/browser/websocket/makeChanges.ts @@ -5,14 +5,13 @@ import type { Change } from "../../deps/socket.ts"; import { toTitleLc } from "../../title.ts"; import { parseYoutube } from "../../parser/youtube.ts"; -export interface Init { +export interface Init extends Page { userId: string; - page: Page; } export function* makeChanges( left: Pick[], right: string[], - { userId, page }: Init, + { userId, ...page }: Init, ): Generator { // 改行文字が入るのを防ぐ const right_ = right.flatMap((text) => text.split("\n")); diff --git a/browser/websocket/patch.ts b/browser/websocket/patch.ts index c0c8693..96ddcf8 100644 --- a/browser/websocket/patch.ts +++ b/browser/websocket/patch.ts @@ -1,21 +1,18 @@ -import { Socket, socketIO, wrap } from "../../deps/socket.ts"; -import { connect, disconnect } from "./socket.ts"; -import { getProjectId, getUserId } from "./id.ts"; +import { Change, DeletePageChange, PinChange } from "../../deps/socket.ts"; import { makeChanges } from "./makeChanges.ts"; -import { pull } from "./pull.ts"; import { Line, Page } from "../../deps/scrapbox-rest.ts"; -import { pushCommit, pushWithRetry } from "./_fetch.ts"; +import { push, PushOptions, RetryError } from "./push.ts"; +import { suggestUnDupTitle } from "./suggestUnDupTitle.ts"; +import { Result } from "../../rest/util.ts"; -export interface PatchOptions { - socket?: Socket; -} +export type PatchOptions = PushOptions; export interface PatchMetadata extends Page { /** 書き換えを再試行した回数 * * 初回は`0`で、再試行するたびに増える */ - retry: number; + attempts: number; } /** ページ全体を書き換える @@ -27,7 +24,7 @@ export interface PatchMetadata extends Page { * @param update 書き換え後の本文を作成する函数。引数には現在の本文が渡される。空配列を返すとページが削除される。undefinedを返すと書き換えを中断する * @param options 使用したいSocketがあれば指定する */ -export const patch = async ( +export const patch = ( project: string, title: string, update: ( @@ -35,69 +32,23 @@ export const patch = async ( metadata: PatchMetadata, ) => string[] | undefined | Promise, options?: PatchOptions, -): Promise => { - const [ - page_, - projectId, - userId, - ] = await Promise.all([ - pull(project, title), - getProjectId(project), - getUserId(), - ]); - - let page = page_; - - const injectedSocket = options?.socket; - const socket = injectedSocket ?? await socketIO(); - await connect(socket); - try { - const { request } = wrap(socket); - - // 3回retryする - for (let retry = 0; retry < 3; retry++) { - try { - const pending = update(page.lines, { ...page, retry }); - const newLines = pending instanceof Promise ? await pending : pending; - - if (!newLines) return; - - if (newLines.length === 0) { - await pushWithRetry(request, [{ deleted: true }], { - projectId, - pageId: page.id, - parentId: page.commitId, - userId, - project, - title, - }); - } - - const changes = [ - ...makeChanges(page.lines, newLines, { userId, page }), - ]; - await pushCommit(request, changes, { - parentId: page.commitId, - projectId, - pageId: page.id, - userId, - }); - break; - } catch (_e: unknown) { - if (retry === 2) { - throw Error("Faild to retry pushing."); - } - console.log( - "Faild to push a commit. Retry after pulling new commits", - ); - try { - page = await pull(project, title); - } catch (e: unknown) { - throw e; - } +): Promise> => + push( + project, + title, + async (page, attempts, prev, reason) => { + if (reason === "DuplicateTitleError") { + const fallbackTitle = suggestUnDupTitle(title); + return prev.map((change) => { + if ("title" in change) change.title = fallbackTitle; + return change; + }) as Change[] | [DeletePageChange] | [PinChange]; } - } - } finally { - if (!injectedSocket) await disconnect(socket); - } -}; + const pending = update(page.lines, { ...page, attempts }); + const newLines = pending instanceof Promise ? await pending : pending; + if (newLines === undefined) return []; + if (newLines.length === 0) return [{ deleted: true }]; + return [...makeChanges(page.lines, newLines, page)]; + }, + options, + ); diff --git a/browser/websocket/pin.ts b/browser/websocket/pin.ts index cd81569..57fb97e 100644 --- a/browser/websocket/pin.ts +++ b/browser/websocket/pin.ts @@ -1,10 +1,8 @@ -import { Socket, socketIO, wrap } from "../../deps/socket.ts"; -import { connect, disconnect } from "./socket.ts"; -import { getProjectId, getUserId } from "./id.ts"; -import { pull } from "./pull.ts"; -import { pushWithRetry } from "./_fetch.ts"; +import { Change, Socket } from "../../deps/socket.ts"; +import { push, PushOptions, RetryError } from "./push.ts"; +import { Result } from "../../rest/util.ts"; -export interface PinOptions { +export interface PinOptions extends PushOptions { /** ピン留め対象のページが存在しないときの振る舞いを変えるoption * * -`true`: タイトルのみのページを作成してピン留めする @@ -13,7 +11,6 @@ export interface PinOptions { * @default false */ create?: boolean; - socket?: Socket; } /** 指定したページをピン留めする * @@ -21,49 +18,26 @@ export interface PinOptions { * @param title ピン留めしたいページのタイトル * @param options 使用したいSocketがあれば指定する */ -export const pin = async ( +export const pin = ( project: string, title: string, options?: PinOptions, -): Promise => { - const [ - page, - projectId, - userId, - ] = await Promise.all([ - pull(project, title), - getProjectId(project), - getUserId(), - ]); - - // 既にピン留めされている場合は何もしない - if (page.pin > 0 || (!page.persistent && !(options?.create ?? false))) return; - - const init = { - parentId: page.commitId, - projectId, - pageId: page.id, - userId, +): Promise> => + push( project, title, - }; - const injectedSocket = options?.socket; - const socket = injectedSocket ?? await socketIO(); - await connect(socket); - const { request } = wrap(socket); - - // タイトルのみのページを作る - if (!page.persistent) { - const commitId = await pushWithRetry(request, [{ title }], init); - init.parentId = commitId; - } - - try { - await pushWithRetry(request, [{ pin: pinNumber() }], init); - } finally { - if (!injectedSocket) await disconnect(socket); - } -}; + (page) => { + // 既にピン留めされている場合は何もしない + if ( + page.pin > 0 || (!page.persistent && !(options?.create ?? false)) + ) return []; + // @ts-ignore 多分ページ作成とピン留めを同時に行っても怒られない……はず + const changes: Change[] = [{ pin: pinNumber() }] as Change[]; + if (!page.persistent) changes.unshift({ title }); + return changes; + }, + options, + ); export interface UnPinOptions { socket?: Socket; @@ -73,43 +47,19 @@ export interface UnPinOptions { * @param project ピン留めを外したいページのproject * @param title ピン留めを外したいページのタイトル */ -export const unpin = async ( +export const unpin = ( project: string, title: string, options: UnPinOptions, -): Promise => { - const [ - page, - projectId, - userId, - ] = await Promise.all([ - pull(project, title), - getProjectId(project), - getUserId(), - ]); - - // 既にピンが外れているか、そもそも存在しないページの場合は何もしない - if (page.pin == 0 || !page.persistent) return; - - const init = { - parentId: page.commitId, - projectId, - pageId: page.id, - userId, +): Promise> => + push( project, title, - }; - const injectedSocket = options?.socket; - const socket = injectedSocket ?? await socketIO(); - await connect(socket); - const { request } = wrap(socket); - - try { - await pushWithRetry(request, [{ pin: 0 }], init); - } finally { - if (!injectedSocket) await disconnect(socket); - } -}; + (page) => + // 既にピンが外れているか、そもそも存在しないページの場合は何もしない + page.pin == 0 || !page.persistent ? [] : [{ pin: 0 }], + options, + ); export const pinNumber = (): number => Number.MAX_SAFE_INTEGER - Math.floor(Date.now() / 1000); diff --git a/browser/websocket/push.ts b/browser/websocket/push.ts new file mode 100644 index 0000000..88e1a33 --- /dev/null +++ b/browser/websocket/push.ts @@ -0,0 +1,157 @@ +import { + Change, + DeletePageChange, + PageCommit, + PageCommitError, + PageCommitResponse, + PinChange, + Socket, + socketIO, + TimeoutError, + UnexpectedError, + wrap, +} from "../../deps/socket.ts"; +import { connect, disconnect } from "./socket.ts"; +import { getProjectId, getUserId } from "./id.ts"; +import { pull } from "./pull.ts"; +import { Page } from "../../deps/scrapbox-rest.ts"; +import { sleep } from "../../sleep.ts"; +import { Result } from "../../rest/util.ts"; + +export interface PushOptions { + /** 外部からSocketを指定したいときに使う */ + socket?: Socket; + + /** pushの再試行回数 + * + * これを上回ったら、`RetryError`を返す + */ + maxAttempts?: number; +} + +export interface RetryError { + name: "RetryError"; + message: string; + attempts: number; +} + +export interface PushMetadata extends Page { + projectId: string; + userId: string; +} + +/** 特定のページのcommitをpushする + * + * serverからpush errorが返ってきた場合、エラーに応じてpushを再試行する + * + * @param project project name + * @param title page title + * @param makeCommit pushしたいcommitを作る関数。空配列を返すとpushを中断する + * @param options + * @retrun 成功 or キャンセルのときは`commitId`を返す。再試行回数が上限に達したときは`RetryError`を返す + */ +export const push = async ( + project: string, + title: string, + makeCommit: ( + page: PushMetadata, + attempts: number, + prev: Change[] | [DeletePageChange] | [PinChange], + reason?: "NotFastForwardError" | "DuplicateTitleError", + ) => + | Promise + | Change[] + | [DeletePageChange] + | [PinChange], + options?: PushOptions, +): Promise> => { + const [ + page_, + projectId, + userId, + ] = await Promise.all([ + pull(project, title), + getProjectId(project), + getUserId(), + ]); + + let page: PushMetadata = { ...page_, projectId, userId }; + + const injectedSocket = options?.socket; + const socket = injectedSocket ?? await socketIO(); + await connect(socket); + + try { + const { request } = wrap(socket); + // loop for create Diff + let attempts = 0; + let changes: Change[] | [DeletePageChange] | [PinChange] = []; + let reason: "NotFastForwardError" | "DuplicateTitleError" | undefined; + while ( + options?.maxAttempts === undefined || attempts < options.maxAttempts + ) { + const pending = makeCommit(page, attempts, changes, reason); + changes = pending instanceof Promise ? await pending : pending; + attempts++; + if (changes.length === 0) { + return { ok: true, value: page.commitId }; + } + + const data: PageCommit = { + kind: "page", + projectId, + pageId: page.id, + parentId: page.commitId, + userId, + changes, + cursor: null, + freeze: true, + }; + + // loop for push changes + while (true) { + const result = (await request("socket.io-request", { + method: "commit", + data, + })) as Result< + PageCommitResponse, + UnexpectedError | TimeoutError | PageCommitError + >; + + if (result.ok) { + page.commitId = result.value.commitId; + // success + return { ok: true, value: page.commitId }; + } + const name = result.value.name; + if (name === "UnexpectedError") { + const error = new Error(); + error.name = result.value.name; + error.message = JSON.stringify(result.value); + throw error; + } + if (name === "TimeoutError" || name === "SocketIOError") { + await sleep(3000); + // go back to the diff loop + break; + } + if (name === "NotFastForwardError") { + page = { ...await pull(project, title), projectId, userId }; + } + reason = name; + // go back to the push loop + } + } + return { + ok: false, + value: { + name: "RetryError", + attempts, + // from https://github.com/denoland/deno_std/blob/0.223.0/async/retry.ts#L23 + message: `Retrying exceeded the maxAttempts (${attempts}).`, + }, + }; + } finally { + if (!injectedSocket) await disconnect(socket); + } +}; diff --git a/browser/websocket/suggestUnDupTitle.test.ts b/browser/websocket/suggestUnDupTitle.test.ts new file mode 100644 index 0000000..b0be4df --- /dev/null +++ b/browser/websocket/suggestUnDupTitle.test.ts @@ -0,0 +1,10 @@ +import { suggestUnDupTitle } from "./suggestUnDupTitle.ts"; +import { assertEquals } from "../../deps/testing.ts"; + +Deno.test("suggestUnDupTitle()", () => { + assertEquals(suggestUnDupTitle("title"), "title_2"); + assertEquals(suggestUnDupTitle("title_2"), "title_3"); + assertEquals(suggestUnDupTitle("title_10"), "title_11"); + assertEquals(suggestUnDupTitle("title_10_3"), "title_10_4"); + assertEquals(suggestUnDupTitle("another_title_5"), "another_title_6"); +}); diff --git a/browser/websocket/suggestUnDupTitle.ts b/browser/websocket/suggestUnDupTitle.ts new file mode 100644 index 0000000..bfc6c19 --- /dev/null +++ b/browser/websocket/suggestUnDupTitle.ts @@ -0,0 +1,6 @@ +export const suggestUnDupTitle = (title: string): string => { + const matched = title.match(/(.+?)(?:_(\d+))?$/); + const title_ = matched?.[1] ?? title; + const num = matched?.[2] ? parseInt(matched[2]) + 1 : 2; + return `${title_}_${num}`; +}; diff --git a/browser/websocket/updateCodeBlock.ts b/browser/websocket/updateCodeBlock.ts index d51ec23..9654aa1 100644 --- a/browser/websocket/updateCodeBlock.ts +++ b/browser/websocket/updateCodeBlock.ts @@ -1,27 +1,14 @@ import { Line } from "../../deps/scrapbox-rest.ts"; -import { - DeleteChange, - InsertChange, - Socket, - socketIO, - UpdateChange, -} from "../../deps/socket.ts"; +import { DeleteChange, InsertChange, UpdateChange } from "../../deps/socket.ts"; import { TinyCodeBlock } from "../../rest/getCodeBlocks.ts"; import { diffToChanges } from "./diffToChanges.ts"; -import { getUserId } from "./id.ts"; import { isSimpleCodeFile } from "./isSimpleCodeFile.ts"; -import { pull } from "./pull.ts"; import { SimpleCodeFile } from "./updateCodeFile.ts"; -import { - applyCommit, - countBodyIndent, - extractFromCodeTitle, -} from "./_codeBlock.ts"; - -export interface UpdateCodeBlockOptions { - /** WebSocketの通信に使うsocket */ - socket?: Socket; +import { countBodyIndent, extractFromCodeTitle } from "./_codeBlock.ts"; +import { push, PushOptions, RetryError } from "./push.ts"; +import { Result } from "../../rest/util.ts"; +export interface UpdateCodeBlockOptions extends PushOptions { /** `true`でデバッグ出力ON */ debug?: boolean; } @@ -35,51 +22,45 @@ export interface UpdateCodeBlockOptions { * @param target 更新対象のコードブロック * @param project 更新対象のコードブロックが存在するプロジェクト名 */ -export const updateCodeBlock = async ( +export const updateCodeBlock = ( newCode: string | string[] | SimpleCodeFile, target: TinyCodeBlock, options?: UpdateCodeBlockOptions, -) => { - /** optionsの既定値はこの中に入れる */ - const defaultOptions: Required = { - socket: options?.socket ?? await socketIO(), - debug: false, - }; - const opt = options ? { ...defaultOptions, ...options } : defaultOptions; - const { projectName, pageTitle } = target.pageInfo; - const [ - head, - userId, - ] = await Promise.all([ - pull(projectName, pageTitle), - getUserId(), - ]); +): Promise> => { const newCodeBody = getCodeBody(newCode); const bodyIndent = countBodyIndent(target); const oldCodeWithoutIndent: Line[] = target.bodyLines.map((e) => { return { ...e, text: e.text.slice(bodyIndent) }; }); - const diffGenerator = diffToChanges(oldCodeWithoutIndent, newCodeBody, { - userId, - }); - const commits = [...fixCommits([...diffGenerator], target)]; - if (isSimpleCodeFile(newCode)) { - const titleCommit = makeTitleChangeCommit(newCode, target); - if (titleCommit) commits.push(titleCommit); - } + return push( + target.pageInfo.projectName, + target.pageInfo.pageTitle, + (page) => { + const diffGenerator = diffToChanges( + oldCodeWithoutIndent, + newCodeBody, + page, + ); + const commits = [...fixCommits([...diffGenerator], target)]; + if (isSimpleCodeFile(newCode)) { + const titleCommit = makeTitleChangeCommit(newCode, target); + if (titleCommit) commits.push(titleCommit); + } - if (opt.debug) { - console.log("%cvvv original code block vvv", "color: limegreen;"); - console.log(target); - console.log("%cvvv new codes vvv", "color: limegreen;"); - console.log(newCode); - console.log("%cvvv commits vvv", "color: limegreen;"); - console.log(commits); - } + if (options?.debug) { + console.log("%cvvv original code block vvv", "color: limegreen;"); + console.log(target); + console.log("%cvvv new codes vvv", "color: limegreen;"); + console.log(newCode); + console.log("%cvvv commits vvv", "color: limegreen;"); + console.log(commits); + } - await applyCommit(commits, head, projectName, pageTitle, opt.socket, userId); - if (!options?.socket) opt.socket.disconnect(); + return commits; + }, + options, + ); }; /** コード本文のテキストを取得する */ diff --git a/browser/websocket/updateCodeFile.ts b/browser/websocket/updateCodeFile.ts index f092e8e..0ccf88c 100644 --- a/browser/websocket/updateCodeFile.ts +++ b/browser/websocket/updateCodeFile.ts @@ -1,16 +1,11 @@ import type { Line } from "../../deps/scrapbox-rest.ts"; -import { - DeleteChange, - InsertChange, - Socket, - socketIO, - UpdateChange, -} from "../../deps/socket.ts"; +import { DeleteChange, InsertChange, UpdateChange } from "../../deps/socket.ts"; import { getCodeBlocks, TinyCodeBlock } from "../../rest/getCodeBlocks.ts"; -import { pull } from "./pull.ts"; -import { createNewLineId, getUserId } from "./id.ts"; +import { createNewLineId } from "./id.ts"; import { diff, toExtendedChanges } from "../../deps/onp.ts"; -import { applyCommit, countBodyIndent } from "./_codeBlock.ts"; +import { countBodyIndent } from "./_codeBlock.ts"; +import { push, PushOptions, RetryError } from "./push.ts"; +import { Result } from "../../rest/util.ts"; /** コードブロックの上書きに使う情報のinterface */ export interface SimpleCodeFile { @@ -25,7 +20,7 @@ export interface SimpleCodeFile { } /** updateCodeFile()に使われているオプション */ -export interface UpdateCodeFileOptions { +export interface UpdateCodeFileOptions extends PushOptions { /** * 指定したファイルが存在しなかった時、新しいコードブロックをページのどの位置に配置するか * @@ -38,9 +33,6 @@ export interface UpdateCodeFileOptions { /** `true`の場合、コードブロック作成時に空行承り太郎(ページ末尾に必ず空行を設ける機能)を有効する(既定は`true`) */ isInsertEmptyLineInTail?: boolean; - /** WebSocketの通信に使うsocket */ - socket?: Socket; - /** `true`でデバッグ出力ON */ debug?: boolean; } @@ -57,51 +49,51 @@ export interface UpdateCodeFileOptions { * @param title 書き換えたいページのタイトル * @param options その他の設定 */ -export const updateCodeFile = async ( +export const updateCodeFile = ( codeFile: SimpleCodeFile, project: string, title: string, options?: UpdateCodeFileOptions, -): Promise => { +): Promise> => { /** optionsの既定値はこの中に入れる */ - const defaultOptions: Required = { + const defaultOptions: Required< + Omit + > = { insertPositionIfNotExist: "notInsert", isInsertEmptyLineInTail: true, - socket: options?.socket ?? await socketIO(), debug: false, }; const opt = options ? { ...defaultOptions, ...options } : defaultOptions; - const newCode = Array.isArray(codeFile.content) - ? codeFile.content - : codeFile.content.split("\n"); - const head = await pull(project, title); - const lines: Line[] = head.lines; - const codeBlocks = await getCodeBlocks({ + + return push( project, title, - lines: lines, - }, { - filename: codeFile.filename, - }); - const commits = [ - ...makeCommits(codeBlocks, codeFile, lines, { - ...opt, - userId: await getUserId(), - }), - ]; - - if (opt.debug) { - console.log("%cvvv original code Blocks vvv", "color: limegreen;"); - console.log(codeBlocks); - console.log("%cvvv new codes vvv", "color: limegreen;"); - console.log(newCode); - console.log("%cvvv commits vvv", "color: limegreen;"); - console.log(commits); - } - - await applyCommit(commits, head, project, title, opt.socket); - - if (!options?.socket) opt.socket.disconnect(); + async (page) => { + const lines: Line[] = page.lines; + const codeBlocks = await getCodeBlocks({ project, title, lines }, { + filename: codeFile.filename, + }); + const commits = [ + ...makeCommits(codeBlocks, codeFile, lines, { + ...opt, + userId: page.userId, + }), + ]; + if (opt.debug) { + console.log("%cvvv original code Blocks vvv", "color: limegreen;"); + console.log(codeBlocks); + console.log("%cvvv new codes vvv", "color: limegreen;"); + const newCode = Array.isArray(codeFile.content) + ? codeFile.content + : codeFile.content.split("\n"); + console.log(newCode); + console.log("%cvvv commits vvv", "color: limegreen;"); + console.log(commits); + } + return commits; + }, + options, + ); }; /** TinyCodeBlocksの配列からコード本文をフラットな配列に格納して返す \ diff --git a/deps/testing.ts b/deps/testing.ts index 2e7f0ce..82a0e65 100644 --- a/deps/testing.ts +++ b/deps/testing.ts @@ -1,2 +1,2 @@ -export * from "https://deno.land/std@0.223.0/testing/asserts.ts"; +export * from "https://deno.land/std@0.223.0/assert/mod.ts"; export * from "https://deno.land/std@0.223.0/testing/snapshot.ts"; From 13c351b01a88ec8b4f0ddedd59d668ae9223de99 Mon Sep 17 00:00:00 2001 From: takker99 <37929109+takker99@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:47:36 +0900 Subject: [PATCH 2/2] build(websocket): Update Deno dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scrapbox-userscript-websocketのバグ修正 --- deps/socket.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/socket.ts b/deps/socket.ts index 3897e3f..0183226 100644 --- a/deps/socket.ts +++ b/deps/socket.ts @@ -1 +1 @@ -export * from "https://raw.githubusercontent.com/takker99/scrapbox-userscript-websocket/0.2.1/mod.ts"; +export * from "https://raw.githubusercontent.com/takker99/scrapbox-userscript-websocket/0.2.2/mod.ts";