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" ;
4
2
import { makeChanges } from "./makeChanges.ts" ;
5
- import { pull } from "./pull.ts" ;
6
3
import { 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" ;
8
7
9
- export interface PatchOptions {
10
- socket ?: Socket ;
11
- }
8
+ export type PatchOptions = PushOptions ;
12
9
13
10
export interface PatchMetadata extends Page {
14
11
/** 書き換えを再試行した回数
15
12
*
16
13
* 初回は`0`で、再試行するたびに増える
17
14
*/
18
- retry : number ;
15
+ attempts : number ;
19
16
}
20
17
21
18
/** ページ全体を書き換える
@@ -27,77 +24,31 @@ export interface PatchMetadata extends Page {
27
24
* @param update 書き換え後の本文を作成する函数。引数には現在の本文が渡される。空配列を返すとページが削除される。undefinedを返すと書き換えを中断する
28
25
* @param options 使用したいSocketがあれば指定する
29
26
*/
30
- export const patch = async (
27
+ export const patch = (
31
28
project : string ,
32
29
title : string ,
33
30
update : (
34
31
lines : Line [ ] ,
35
32
metadata : PatchMetadata ,
36
33
) => string [ ] | undefined | Promise < string [ ] | undefined > ,
37
34
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 ] ;
98
46
}
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