1- import { Socket , socketIO , wrap } from "../../deps/socket.ts" ;
2- import { connect , disconnect } from "./socket.ts" ;
3- import { getProjectId , getUserId } from "./id.ts" ;
1+ import { Change , DeletePageChange , PinChange } from "../../deps/socket.ts" ;
42import { makeChanges } from "./makeChanges.ts" ;
5- import { pull } from "./pull.ts" ;
63import { Line , Page } from "../../deps/scrapbox-rest.ts" ;
7- import { pushCommit , pushWithRetry } from "./_fetch.ts" ;
4+ import { push , PushOptions , RetryError } from "./push.ts" ;
5+ import { suggestUnDupTitle } from "./suggestUnDupTitle.ts" ;
6+ import { Result } from "../../rest/util.ts" ;
87
9- export interface PatchOptions {
10- socket ?: Socket ;
11- }
8+ export type PatchOptions = PushOptions ;
129
1310export interface PatchMetadata extends Page {
1411 /** 書き換えを再試行した回数
1512 *
1613 * 初回は`0`で、再試行するたびに増える
1714 */
18- retry : number ;
15+ attempts : number ;
1916}
2017
2118/** ページ全体を書き換える
@@ -27,77 +24,31 @@ export interface PatchMetadata extends Page {
2724 * @param update 書き換え後の本文を作成する函数。引数には現在の本文が渡される。空配列を返すとページが削除される。undefinedを返すと書き換えを中断する
2825 * @param options 使用したいSocketがあれば指定する
2926 */
30- export const patch = async (
27+ export const patch = (
3128 project : string ,
3229 title : string ,
3330 update : (
3431 lines : Line [ ] ,
3532 metadata : PatchMetadata ,
3633 ) => string [ ] | undefined | Promise < string [ ] | undefined > ,
3734 options ?: PatchOptions ,
38- ) : Promise < void > => {
39- const [
40- page_ ,
41- projectId ,
42- userId ,
43- ] = await Promise . all ( [
44- pull ( project , title ) ,
45- getProjectId ( project ) ,
46- getUserId ( ) ,
47- ] ) ;
48-
49- let page = page_ ;
50-
51- const injectedSocket = options ?. socket ;
52- const socket = injectedSocket ?? await socketIO ( ) ;
53- await connect ( socket ) ;
54- try {
55- const { request } = wrap ( socket ) ;
56-
57- // 3回retryする
58- for ( let retry = 0 ; retry < 3 ; retry ++ ) {
59- try {
60- const pending = update ( page . lines , { ...page , retry } ) ;
61- const newLines = pending instanceof Promise ? await pending : pending ;
62-
63- if ( ! newLines ) return ;
64-
65- if ( newLines . length === 0 ) {
66- await pushWithRetry ( request , [ { deleted : true } ] , {
67- projectId,
68- pageId : page . id ,
69- parentId : page . commitId ,
70- userId,
71- project,
72- title,
73- } ) ;
74- }
75-
76- const changes = [
77- ...makeChanges ( page . lines , newLines , { userId, page } ) ,
78- ] ;
79- await pushCommit ( request , changes , {
80- parentId : page . commitId ,
81- projectId,
82- pageId : page . id ,
83- userId,
84- } ) ;
85- break ;
86- } catch ( _e : unknown ) {
87- if ( retry === 2 ) {
88- throw Error ( "Faild to retry pushing." ) ;
89- }
90- console . log (
91- "Faild to push a commit. Retry after pulling new commits" ,
92- ) ;
93- try {
94- page = await pull ( project , title ) ;
95- } catch ( e : unknown ) {
96- throw e ;
97- }
35+ ) : Promise < Result < string , RetryError > > =>
36+ push (
37+ project ,
38+ title ,
39+ async ( page , attempts , prev , reason ) => {
40+ if ( reason === "DuplicateTitleError" ) {
41+ const fallbackTitle = suggestUnDupTitle ( title ) ;
42+ return prev . map ( ( change ) => {
43+ if ( "title" in change ) change . title = fallbackTitle ;
44+ return change ;
45+ } ) as Change [ ] | [ DeletePageChange ] | [ PinChange ] ;
9846 }
99- }
100- } finally {
101- if ( ! injectedSocket ) await disconnect ( socket ) ;
102- }
103- } ;
47+ const pending = update ( page . lines , { ...page , attempts } ) ;
48+ const newLines = pending instanceof Promise ? await pending : pending ;
49+ if ( newLines === undefined ) return [ ] ;
50+ if ( newLines . length === 0 ) return [ { deleted : true } ] ;
51+ return [ ...makeChanges ( page . lines , newLines , page ) ] ;
52+ } ,
53+ options ,
54+ ) ;
0 commit comments