first commit

This commit is contained in:
9700X_PC 2025-10-13 07:06:50 +09:00
commit 0f6382b992
60 changed files with 57757 additions and 0 deletions

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
/lib
/Lib
/Scripts
/include
/pip-wheel-metadata
/pyvenv.cfg
/venv
/Scripts
/include
/pip-wheel-metadata
/pyvenv.cfg
pyvenv.cfg
*.pyc
*.pyo
*.pyd
*.pyw
*.pyz
*.pywz
*.pyzw
*.pyzwz
*.pyzwzw
*.pyzwzwz
*.log

135
200-mmi.csv Normal file
View File

@ -0,0 +1,135 @@
code_name,code_description,data_type,car_id,alias_name,manufacturer
ACK_WHD1,TACHO1 설정 차륜경,데이터,5,우진200량-3단계,Woojin
ACK_WHD2,TACHO2 설정 차륜경,데이터,5,우진200량-3단계,Woojin
ADOL,왼쪽 출입문 열림명령,데이터,5,우진200량-3단계,Woojin
ADOR,오른쪽 출입문 열림명령,데이터,5,우진200량-3단계,Woojin
ATO_EB_REQ,ATO 비상제동 요구,데이터,5,우진200량-3단계,Woojin
ATO_LIMITSPEED,ATO 제한속도,데이터,5,우진200량-3단계,Woojin
ATO_ERR_MSG,ATO 에러 메시지,데이터,5,우진200량-3단계,Woojin
ATO_ERR_DETECT,ATO 에러 검지,데이터,5,우진200량-3단계,Woojin
ATO_START_BTN,출발 버튼 취급,데이터,5,우진200량-3단계,Woojin
ATC_CARRIER_F,ATC 캐리어 주파수,데이터,5,우진200량-3단계,Woojin
ATC_CODE,ATC 코드,데이터,5,우진200량-3단계,Woojin
ATC_CODE_CARRIER_F,ATC 코드 캐리어 주파수,데이터,5,우진200량-3단계,Woojin
ATC_CODE_F,ATC 코드 주파수,데이터,5,우진200량-3단계,Woojin
ATC_ERR_MSG,ATC 에러 메시지,데이터,5,우진200량-3단계,Woojin
ATC_STATUS,ATC 상태,데이터,5,우진200량-3단계,Woojin
ATC_SWVER,ATC SW 버전,데이터,5,우진200량-3단계,Woojin
AUTO,운전모드 AUTO,데이터,5,우진200량-3단계,Woojin
DIAGNOSTIC_ERR_MSG,다이그노스틱 에러 메시지,데이터,5,우진200량-3단계,Woojin
DOORMOD,출입문 모드,데이터,5,우진200량-3단계,Woojin
DOOR_CLOSE,출입문 닫힘,데이터,5,우진200량-3단계,Woojin
DOOR_CLOSE_WARNING,출입문 닫힘 경고,데이터,5,우진200량-3단계,Woojin
DOOR_OPEN,출입문 열림,데이터,5,우진200량-3단계,Woojin
DO_ZVR,ZVR 출력,데이터,5,우진200량-3단계,Woojin
DO_EDL,EDL 출력,데이터,5,우진200량-3단계,Woojin
DO_EDR,EDR 출력,데이터,5,우진200량-3단계,Woojin
DO_FSB,FSB 출력,데이터,5,우진200량-3단계,Woojin
DO_EBP,EB(+) 출력,데이터,5,우진200량-3단계,Woojin
DO_EBM,EB(-) 출력,데이터,5,우진200량-3단계,Woojin
DSTN,TWC 종착역,데이터,5,우진200량-3단계,Woojin
DTG,남은거리,데이터,5,우진200량-3단계,Woojin
FA,운전모드 FA,데이터,5,우진200량-3단계,Woojin
F1,ATC 캐리어 F1,데이터,5,우진200량-3단계,Woojin
F2,ATC 캐리어 F2,데이터,5,우진200량-3단계,Woojin
F3,ATC 캐리어 F3,데이터,5,우진200량-3단계,Woojin
F4,ATC 캐리어 F4,데이터,5,우진200량-3단계,Woojin
FAIL_ATCR,ATCR 통신 실패,데이터,5,우진200량-3단계,Woojin
FAIL_ATOC,ATOC 통신실패,데이터,5,우진200량-3단계,Woojin
FAIL_TACHO1,TACHO1 통신실패,데이터,5,우진200량-3단계,Woojin
FAIL_TACHO2,TACHO2 통신실패,데이터,5,우진200량-3단계,Woojin
FAIL_TCMS,TCMS 통신실패,데이터,5,우진200량-3단계,Woojin
FORMNO,편성 번호,데이터,5,우진200량-3단계,Woojin
FMC,운전모드 FMC,데이터,5,우진200량-3단계,Woojin
HCR,선두차 지정,데이터,5,우진200량-3단계,Woojin
INTERFACE_ERR_MSG,인터페이스 에러 메시지,데이터,5,우진200량-3단계,Woojin
INCHING,정위치 정밀제어,데이터,5,우진200량-3단계,Woojin
IPDT_NG,이니셜PDT NG,데이터,5,우진200량-3단계,Woojin
IPDT_OK,이니셜PDT OK,데이터,5,우진200량-3단계,Woojin
KUR,KEY UP RELAY,데이터,5,우진200량-3단계,Woojin
LIMITSPEED,ATC 제한속도,데이터,5,우진200량-3단계,Woojin
LIMIT_DRIVE,가속제한,데이터,5,우진200량-3단계,Woojin
MARKER,PG 마커,데이터,5,우진200량-3단계,Woojin
MASCON_BR,마스콘 제동,데이터,5,우진200량-3단계,Woojin
MASCON_DR,마스콘 추진,데이터,5,우진200량-3단계,Woojin
MASCON_EB,마스콘 비상,데이터,5,우진200량-3단계,Woojin
MASCON_NEU,마스콘 중립,데이터,5,우진200량-3단계,Woojin
MCS,운전모드 MCS,데이터,5,우진200량-3단계,Woojin
MPDT_NG,매뉴얼PDT NG,데이터,5,우진200량-3단계,Woojin
MPDT_OK,매뉴얼PDT OK,데이터,5,우진200량-3단계,Woojin
MPDT_START,매뉴얼PDT START,데이터,5,우진200량-3단계,Woojin
NEXTDOOR,다음역 출입문 방향,데이터,5,우진200량-3단계,Woojin
NOMAL,평상모드,데이터,5,우진200량-3단계,Woojin
NSTN,TWC 다음역,데이터,5,우진200량-3단계,Woojin
OVER_SPD_WARNING,과속 경고,데이터,5,우진200량-3단계,Woojin
OSC_F0_OK,OSC 상시주파수 OK,데이터,5,우진200량-3단계,Woojin
OSC_F,OSC 주파수,데이터,5,우진200량-3단계,Woojin
OV_STOP2,70이상 초과,데이터,5,우진200량-3단계,Woojin
PRE_BRAKE,사전제동,데이터,5,우진200량-3단계,Woojin
PWM_VALUE,PWM 값,데이터,5,우진200량-3단계,Woojin
PSD_OPEN,PSD 열림,데이터,5,우진200량-3단계,Woojin
PSD_CLOSE,PSD 닫힘,데이터,5,우진200량-3단계,Woojin
PSTN,TWC 현재역,데이터,5,우진200량-3단계,Woojin
RECOVERY,회복모드,데이터,5,우진200량-3단계,Woojin
REVERSINGROD_ FWD,역전기 전진,데이터,5,우진200량-3단계,Woojin
REVERSINGROD_ NEU,역전기 중립,데이터,5,우진200량-3단계,Woojin
REVERSINGROD_RVS,역전기 후진,데이터,5,우진200량-3단계,Woojin
SEQ,시퀀스 넘버,데이터,5,우진200량-3단계,Woojin
SH_STOP2,70이상 미달,데이터,5,우진200량-3단계,Woojin
START_ENABLE,출발 허용 조건,데이터,5,우진200량-3단계,Woojin
SYSTEM_ACTIVE,장치 상태,데이터,5,우진200량-3단계,Woojin
TRAINSPEED,열차 속도,데이터,5,우진200량-3단계,Woojin
TRAINSPEED_A,TACHO1 속도,데이터,5,우진200량-3단계,Woojin
TRAINSPEED_B,TACHO2 속도,데이터,5,우진200량-3단계,Woojin
TRAINNO,열 번,데이터,5,우진200량-3단계,Woojin
TASC,타스크,데이터,5,우진200량-3단계,Woojin
TASC_DB,가상 타스크,데이터,5,우진200량-3단계,Woojin
TACHO_DIR_A,TACHO1 방향,데이터,5,우진200량-3단계,Woojin
TACHO_DIR_B,TACHO2 방향,데이터,5,우진200량-3단계,Woojin
TCMSDOOR,TCMS 출입문 정보,데이터,5,우진200량-3단계,Woojin
TC1,1호차,데이터,5,우진200량-3단계,Woojin
TC2,8호차,데이터,5,우진200량-3단계,Woojin
TRAC_BR,추진 명령,데이터,5,우진200량-3단계,Woojin
TRAC_CS,타행 명령,데이터,5,우진200량-3단계,Woojin
TRAC_DR,제동 명령,데이터,5,우진200량-3단계,Woojin
TRAINBERTH,정위치 정차,데이터,5,우진200량-3단계,Woojin
TCR,꼬리차 지정,데이터,5,우진200량-3단계,Woojin
TIME,시간,데이터,5,우진200량-3단계,Woojin
TWCT_ENABLE,TWC 송신 허용,데이터,5,우진200량-3단계,Woojin
WHEELCHECK,차륜경 검증,데이터,5,우진200량-3단계,Woojin
WRONGDOOR,TWC 출입문 방향불일치,데이터,5,우진200량-3단계,Woojin
YARD,운전모드 YARD,데이터,5,우진200량-3단계,Woojin
ADC(Automatic Close Door),출입문 자동 닫힘 제어 명령,약어,5,우진200량-3단계,Woojin
ADOL(Automatic Open Left Door),"왼쪽 출입문 자동 열림 제어 명령(MCS, AUTO, FA)",약어,5,우진200량-3단계,Woojin
ADOR(Automatic Open Right Door),"오른쪽 출입문 자동 열림 제어 명령(MCS, AUTO, FA)",약어,5,우진200량-3단계,Woojin
ATO EB REQ(ATO Emergency Brake Request),ATO 비상제동 요청,약어,5,우진200량-3단계,Woojin
BR(Brake Relay),제동 릴레이,약어,5,우진200량-3단계,Woojin
DR(Drive Relay),추진 릴레이,약어,5,우진200량-3단계,Woojin
CS(Coast),타행,약어,5,우진200량-3단계,Woojin
CSC(Constant Control Speed),정속 운행,약어,5,우진200량-3단계,Woojin
DCW(Door Close Warning),출입문 닫힘 경고,약어,5,우진200량-3단계,Woojin
DIA1 ACK(Ack for Wheel DIA1 Value),차륜경 설정 1값의 인정,약어,5,우진200량-3단계,Woojin
DIA2 ACK(Ack for Wheel DIA2 Value),차륜경 설정 2값의 인정,약어,5,우진200량-3단계,Woojin
EB(Emergency Brake),비상제동,약어,5,우진200량-3단계,Woojin
EDL(Enable Door Left),왼쪽 출입문 열림 가능,약어,5,우진200량-3단계,Woojin
EDR(Enable Door Right),오른쪽 출입문 열림 가능,약어,5,우진200량-3단계,Woojin
FSB(Full Service Brake),상용만제동,약어,5,우진200량-3단계,Woojin
HCR(Head Control Relay),선두차 지정 입력신호,약어,5,우진200량-3단계,Woojin
INCHING(Inching),정밀 제어 동작,약어,5,우진200량-3단계,Woojin
INITIAL PDT(Initial PDT),장치 기동  자동 PDT 시험,약어,5,우진200량-3단계,Woojin
KUR(Key Up Relay),키 업 릴레이(불용),약어,5,우진200량-3단계,Woojin
MANUAL PDT(Manual PDT),수동 PDT 시험,약어,5,우진200량-3단계,Woojin
OSC(OSC Relay),OSC 안테나 선택 릴레이(1계/2계),약어,5,우진200량-3단계,Woojin
OvrSPD Warning(Over Speed Warining),ATC 과속 경고,약어,5,우진200량-3단계,Woojin
SEL(Start Enable Lamp),AUTO 출발버튼 램프,약어,5,우진200량-3단계,Woojin
SYSTEM ACTIVE(System Active),"1,2계 시스템   계(MAIN)를 표현",약어,5,우진200량-3단계,Woojin
TACHO DIR#A(Tacho Direction A),DI#A에 입력되는 전방 TACHO 방향(ON:FWD / OFF:RVS),약어,5,우진200량-3단계,Woojin
TACHO DIR#B(Tacho Direction B),DI#B에 입력되는 후방 TACHO 방향(ON:FWD / OFF:RVS),약어,5,우진200량-3단계,Woojin
TASC(Train Automatic Stop Control),TASC 제어모드,약어,5,우진200량-3단계,Woojin
TASC DB(Train Automatic Stop Control Data Base),DB TASC 제어모드,약어,5,우진200량-3단계,Woojin
TCR(Tale Control Relay),후미차 지정 입력신호,약어,5,우진200량-3단계,Woojin
TRAIN BERTH(Train Berth),정위치 정차,약어,5,우진200량-3단계,Woojin
TWCT x Enable(TWC TX Enable),TWCT 차상>지상 송신 허용,약어,5,우진200량-3단계,Woojin
WRONG Door(TWC Door Direction Wrong),지상 출입문 방향 정보와 차상 출입문 방향 정보가 다를 때 알림,약어,5,우진200량-3단계,Woojin
가속제한(Acceleration Limitation),추진 중 정해진 속도코드보다 목표속도를 제한하여 제동 충격 방지,약어,5,우진200량-3단계,Woojin
사전제동(Pre-Braking),운행 간 속도코드 변환 지점에 맞춰 사전에 제동하여 충격 방지,약어,5,우진200량-3단계,Woojin
1 code_name code_description data_type car_id alias_name manufacturer
2 ACK_WHD1 TACHO1 설정 차륜경 데이터 5 우진200량-3단계 Woojin
3 ACK_WHD2 TACHO2 설정 차륜경 데이터 5 우진200량-3단계 Woojin
4 ADOL 왼쪽 출입문 열림명령 데이터 5 우진200량-3단계 Woojin
5 ADOR 오른쪽 출입문 열림명령 데이터 5 우진200량-3단계 Woojin
6 ATO_EB_REQ ATO 비상제동 요구 데이터 5 우진200량-3단계 Woojin
7 ATO_LIMITSPEED ATO 제한속도 데이터 5 우진200량-3단계 Woojin
8 ATO_ERR_MSG ATO 에러 메시지 데이터 5 우진200량-3단계 Woojin
9 ATO_ERR_DETECT ATO 에러 검지 데이터 5 우진200량-3단계 Woojin
10 ATO_START_BTN 출발 버튼 취급 데이터 5 우진200량-3단계 Woojin
11 ATC_CARRIER_F ATC 캐리어 주파수 데이터 5 우진200량-3단계 Woojin
12 ATC_CODE ATC 코드 데이터 5 우진200량-3단계 Woojin
13 ATC_CODE_CARRIER_F ATC 코드 캐리어 주파수 데이터 5 우진200량-3단계 Woojin
14 ATC_CODE_F ATC 코드 주파수 데이터 5 우진200량-3단계 Woojin
15 ATC_ERR_MSG ATC 에러 메시지 데이터 5 우진200량-3단계 Woojin
16 ATC_STATUS ATC 상태 데이터 5 우진200량-3단계 Woojin
17 ATC_SWVER ATC SW 버전 데이터 5 우진200량-3단계 Woojin
18 AUTO 운전모드 AUTO 데이터 5 우진200량-3단계 Woojin
19 DIAGNOSTIC_ERR_MSG 다이그노스틱 에러 메시지 데이터 5 우진200량-3단계 Woojin
20 DOORMOD 출입문 모드 데이터 5 우진200량-3단계 Woojin
21 DOOR_CLOSE 출입문 닫힘 데이터 5 우진200량-3단계 Woojin
22 DOOR_CLOSE_WARNING 출입문 닫힘 경고 데이터 5 우진200량-3단계 Woojin
23 DOOR_OPEN 출입문 열림 데이터 5 우진200량-3단계 Woojin
24 DO_ZVR ZVR 출력 데이터 5 우진200량-3단계 Woojin
25 DO_EDL EDL 출력 데이터 5 우진200량-3단계 Woojin
26 DO_EDR EDR 출력 데이터 5 우진200량-3단계 Woojin
27 DO_FSB FSB 출력 데이터 5 우진200량-3단계 Woojin
28 DO_EBP EB(+) 출력 데이터 5 우진200량-3단계 Woojin
29 DO_EBM EB(-) 출력 데이터 5 우진200량-3단계 Woojin
30 DSTN TWC 종착역 데이터 5 우진200량-3단계 Woojin
31 DTG 남은거리 데이터 5 우진200량-3단계 Woojin
32 FA 운전모드 FA 데이터 5 우진200량-3단계 Woojin
33 F1 ATC 캐리어 F1 데이터 5 우진200량-3단계 Woojin
34 F2 ATC 캐리어 F2 데이터 5 우진200량-3단계 Woojin
35 F3 ATC 캐리어 F3 데이터 5 우진200량-3단계 Woojin
36 F4 ATC 캐리어 F4 데이터 5 우진200량-3단계 Woojin
37 FAIL_ATCR ATCR 통신 실패 데이터 5 우진200량-3단계 Woojin
38 FAIL_ATOC ATOC 통신실패 데이터 5 우진200량-3단계 Woojin
39 FAIL_TACHO1 TACHO1 통신실패 데이터 5 우진200량-3단계 Woojin
40 FAIL_TACHO2 TACHO2 통신실패 데이터 5 우진200량-3단계 Woojin
41 FAIL_TCMS TCMS 통신실패 데이터 5 우진200량-3단계 Woojin
42 FORMNO 편성 번호 데이터 5 우진200량-3단계 Woojin
43 FMC 운전모드 FMC 데이터 5 우진200량-3단계 Woojin
44 HCR 선두차 지정 데이터 5 우진200량-3단계 Woojin
45 INTERFACE_ERR_MSG 인터페이스 에러 메시지 데이터 5 우진200량-3단계 Woojin
46 INCHING 정위치 정밀제어 데이터 5 우진200량-3단계 Woojin
47 IPDT_NG 이니셜PDT NG 데이터 5 우진200량-3단계 Woojin
48 IPDT_OK 이니셜PDT OK 데이터 5 우진200량-3단계 Woojin
49 KUR KEY UP RELAY 데이터 5 우진200량-3단계 Woojin
50 LIMITSPEED ATC 제한속도 데이터 5 우진200량-3단계 Woojin
51 LIMIT_DRIVE 가속제한 데이터 5 우진200량-3단계 Woojin
52 MARKER PG 마커 데이터 5 우진200량-3단계 Woojin
53 MASCON_BR 마스콘 제동 데이터 5 우진200량-3단계 Woojin
54 MASCON_DR 마스콘 추진 데이터 5 우진200량-3단계 Woojin
55 MASCON_EB 마스콘 비상 데이터 5 우진200량-3단계 Woojin
56 MASCON_NEU 마스콘 중립 데이터 5 우진200량-3단계 Woojin
57 MCS 운전모드 MCS 데이터 5 우진200량-3단계 Woojin
58 MPDT_NG 매뉴얼PDT NG 데이터 5 우진200량-3단계 Woojin
59 MPDT_OK 매뉴얼PDT OK 데이터 5 우진200량-3단계 Woojin
60 MPDT_START 매뉴얼PDT START 데이터 5 우진200량-3단계 Woojin
61 NEXTDOOR 다음역 출입문 방향 데이터 5 우진200량-3단계 Woojin
62 NOMAL 평상모드 데이터 5 우진200량-3단계 Woojin
63 NSTN TWC 다음역 데이터 5 우진200량-3단계 Woojin
64 OVER_SPD_WARNING 과속 경고 데이터 5 우진200량-3단계 Woojin
65 OSC_F0_OK OSC 상시주파수 OK 데이터 5 우진200량-3단계 Woojin
66 OSC_F OSC 주파수 데이터 5 우진200량-3단계 Woojin
67 OV_STOP2 70이상 초과 데이터 5 우진200량-3단계 Woojin
68 PRE_BRAKE 사전제동 데이터 5 우진200량-3단계 Woojin
69 PWM_VALUE PWM 값 데이터 5 우진200량-3단계 Woojin
70 PSD_OPEN PSD 열림 데이터 5 우진200량-3단계 Woojin
71 PSD_CLOSE PSD 닫힘 데이터 5 우진200량-3단계 Woojin
72 PSTN TWC 현재역 데이터 5 우진200량-3단계 Woojin
73 RECOVERY 회복모드 데이터 5 우진200량-3단계 Woojin
74 REVERSINGROD_ FWD 역전기 전진 데이터 5 우진200량-3단계 Woojin
75 REVERSINGROD_ NEU 역전기 중립 데이터 5 우진200량-3단계 Woojin
76 REVERSINGROD_RVS 역전기 후진 데이터 5 우진200량-3단계 Woojin
77 SEQ 시퀀스 넘버 데이터 5 우진200량-3단계 Woojin
78 SH_STOP2 70이상 미달 데이터 5 우진200량-3단계 Woojin
79 START_ENABLE 출발 허용 조건 데이터 5 우진200량-3단계 Woojin
80 SYSTEM_ACTIVE 장치 상태 데이터 5 우진200량-3단계 Woojin
81 TRAINSPEED 열차 속도 데이터 5 우진200량-3단계 Woojin
82 TRAINSPEED_A TACHO1 속도 데이터 5 우진200량-3단계 Woojin
83 TRAINSPEED_B TACHO2 속도 데이터 5 우진200량-3단계 Woojin
84 TRAINNO 열 번 데이터 5 우진200량-3단계 Woojin
85 TASC 타스크 데이터 5 우진200량-3단계 Woojin
86 TASC_DB 가상 타스크 데이터 5 우진200량-3단계 Woojin
87 TACHO_DIR_A TACHO1 방향 데이터 5 우진200량-3단계 Woojin
88 TACHO_DIR_B TACHO2 방향 데이터 5 우진200량-3단계 Woojin
89 TCMSDOOR TCMS 출입문 정보 데이터 5 우진200량-3단계 Woojin
90 TC1 1호차 데이터 5 우진200량-3단계 Woojin
91 TC2 8호차 데이터 5 우진200량-3단계 Woojin
92 TRAC_BR 추진 명령 데이터 5 우진200량-3단계 Woojin
93 TRAC_CS 타행 명령 데이터 5 우진200량-3단계 Woojin
94 TRAC_DR 제동 명령 데이터 5 우진200량-3단계 Woojin
95 TRAINBERTH 정위치 정차 데이터 5 우진200량-3단계 Woojin
96 TCR 꼬리차 지정 데이터 5 우진200량-3단계 Woojin
97 TIME 시간 데이터 5 우진200량-3단계 Woojin
98 TWCT_ENABLE TWC 송신 허용 데이터 5 우진200량-3단계 Woojin
99 WHEELCHECK 차륜경 검증 데이터 5 우진200량-3단계 Woojin
100 WRONGDOOR TWC 출입문 방향불일치 데이터 5 우진200량-3단계 Woojin
101 YARD 운전모드 YARD 데이터 5 우진200량-3단계 Woojin
102 ADC(Automatic Close Door) 출입문 자동 닫힘 제어 명령 약어 5 우진200량-3단계 Woojin
103 ADOL(Automatic Open Left Door) 왼쪽 출입문 자동 열림 제어 명령(MCS, AUTO, FA) 약어 5 우진200량-3단계 Woojin
104 ADOR(Automatic Open Right Door) 오른쪽 출입문 자동 열림 제어 명령(MCS, AUTO, FA) 약어 5 우진200량-3단계 Woojin
105 ATO EB REQ(ATO Emergency Brake Request) ATO 비상제동 요청 약어 5 우진200량-3단계 Woojin
106 BR(Brake Relay) 제동 릴레이 약어 5 우진200량-3단계 Woojin
107 DR(Drive Relay) 추진 릴레이 약어 5 우진200량-3단계 Woojin
108 CS(Coast) 타행 약어 5 우진200량-3단계 Woojin
109 CSC(Constant Control Speed) 정속 운행 약어 5 우진200량-3단계 Woojin
110 DCW(Door Close Warning) 출입문 닫힘 경고 약어 5 우진200량-3단계 Woojin
111 DIA1 ACK(Ack for Wheel DIA1 Value) 차륜경 설정 1값의 인정 약어 5 우진200량-3단계 Woojin
112 DIA2 ACK(Ack for Wheel DIA2 Value) 차륜경 설정 2값의 인정 약어 5 우진200량-3단계 Woojin
113 EB(Emergency Brake) 비상제동 약어 5 우진200량-3단계 Woojin
114 EDL(Enable Door Left) 왼쪽 출입문 열림 가능 약어 5 우진200량-3단계 Woojin
115 EDR(Enable Door Right) 오른쪽 출입문 열림 가능 약어 5 우진200량-3단계 Woojin
116 FSB(Full Service Brake) 상용만제동 약어 5 우진200량-3단계 Woojin
117 HCR(Head Control Relay) 선두차 지정 입력신호 약어 5 우진200량-3단계 Woojin
118 INCHING(Inching) 정밀 제어 동작 약어 5 우진200량-3단계 Woojin
119 INITIAL PDT(Initial PDT) 장치 기동 시 자동 PDT 시험 약어 5 우진200량-3단계 Woojin
120 KUR(Key Up Relay) 키 업 릴레이(불용) 약어 5 우진200량-3단계 Woojin
121 MANUAL PDT(Manual PDT) 수동 PDT 시험 약어 5 우진200량-3단계 Woojin
122 OSC(OSC Relay) OSC 안테나 선택 릴레이(1계/2계) 약어 5 우진200량-3단계 Woojin
123 OvrSPD Warning(Over Speed Warining) ATC 과속 경고 약어 5 우진200량-3단계 Woojin
124 SEL(Start Enable Lamp) AUTO 출발버튼 램프 약어 5 우진200량-3단계 Woojin
125 SYSTEM ACTIVE(System Active) 1,2계 시스템 중 주 계(MAIN)를 표현 약어 5 우진200량-3단계 Woojin
126 TACHO DIR#A(Tacho Direction A) DI#A에 입력되는 전방 TACHO 방향(ON:FWD / OFF:RVS) 약어 5 우진200량-3단계 Woojin
127 TACHO DIR#B(Tacho Direction B) DI#B에 입력되는 후방 TACHO 방향(ON:FWD / OFF:RVS) 약어 5 우진200량-3단계 Woojin
128 TASC(Train Automatic Stop Control) TASC 제어모드 약어 5 우진200량-3단계 Woojin
129 TASC DB(Train Automatic Stop Control Data Base) DB TASC 제어모드 약어 5 우진200량-3단계 Woojin
130 TCR(Tale Control Relay) 후미차 지정 입력신호 약어 5 우진200량-3단계 Woojin
131 TRAIN BERTH(Train Berth) 정위치 정차 약어 5 우진200량-3단계 Woojin
132 TWCT x Enable(TWC TX Enable) TWCT 차상>지상 송신 허용 약어 5 우진200량-3단계 Woojin
133 WRONG Door(TWC Door Direction Wrong) 지상 출입문 방향 정보와 차상 출입문 방향 정보가 다를 때 알림 약어 5 우진200량-3단계 Woojin
134 가속제한(Acceleration Limitation) 추진 중 정해진 속도코드보다 목표속도를 제한하여 제동 충격 방지 약어 5 우진200량-3단계 Woojin
135 사전제동(Pre-Braking) 운행 간 속도코드 변환 지점에 맞춰 사전에 제동하여 충격 방지 약어 5 우진200량-3단계 Woojin

0
43576520 Normal file
View File

BIN
FaultCode.xlsx Normal file

Binary file not shown.

37
README.md Normal file
View File

@ -0,0 +1,37 @@
1호선 고장코드 (Flask + HTMX, Supabase)
실행 방법
1) 의존성 설치 (venv 권장)
```
pip install -r requirements.txt
```
2) 환경변수 설정
```
# Windows PowerShell 예시
$env:SUPABASE_URL="http://192.168.0.180:54321" # 로컬 도커 기본 포트
$env:SUPABASE_ANON_KEY="<ANON_KEY>"
```
3) 서버 실행
```
python app.py
```
브라우저에서 http://localhost:5000 접속
기능
- Supabase의 `public.Falut_Code_Table`에서 필터/검색
- 항목 클릭 시 모달 상세 표시
- 다크모드 토글
비고
- 로컬 SQLite 및 `/api/meta`, `/api/db` 기반 동기화는 제거됨
- Supabase 접속 실패 시 안내 메시지를 화면에 표시
- 사내망 주소/포트 방화벽 허용 필요 (예: 54321, 8000 등 구성에 따라 상이)

542
app.py Normal file
View File

@ -0,0 +1,542 @@
import os
from typing import Optional, List, Dict
import httpx
from flask import Flask, g, render_template, request, abort
from urllib.parse import urlencode
from dotenv import load_dotenv
APP_NAME = "1호선 고장코드"
def create_app() -> Flask:
app = Flask(__name__)
app.config.update(TEMPLATES_AUTO_RELOAD=True)
# 환경변수 로드 및 Supabase 기본값 설정
load_dotenv()
# 기본: Kong 프록시(8000) 또는 사용자가 지정한 URL
app.config.setdefault("SUPABASE_URL", os.environ.get("SUPABASE_URL", "http://192.168.0.180:8000"))
app.config.setdefault("SUPABASE_ANON_KEY", os.environ.get("SUPABASE_ANON_KEY", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzU4NTUxNjY2LCJleHAiOjQxMDI0NDQ4MDB9.jMCGL3Q-N2o_l7JQE_HrO7Uoct86CMgLsVxpabisG4I"))
# Kong Basic Auth(선택)
app.config.setdefault("SUPABASE_BASIC_USER", os.environ.get("SUPABASE_BASIC_USER", ""))
app.config.setdefault("SUPABASE_BASIC_PASSWORD", os.environ.get("SUPABASE_BASIC_PASSWORD", ""))
# 더 이상 SQLite 초기화/연결을 사용하지 않음 (Supabase만 사용)
# PostgREST(REST) 클라이언트 빌더
def build_pg_client() -> httpx.Client:
base = app.config["SUPABASE_URL"].rstrip("/") + "/rest/v1"
basic_user = app.config.get("SUPABASE_BASIC_USER") or ""
basic_pass = app.config.get("SUPABASE_BASIC_PASSWORD") or ""
headers = {
"apikey": app.config.get("SUPABASE_ANON_KEY", ""),
"Accept-Profile": "public",
"Content-Profile": "public",
}
auth = httpx.BasicAuth(basic_user, basic_pass) if basic_user else None
if not auth and app.config.get("SUPABASE_ANON_KEY"):
headers["Authorization"] = f"Bearer {app.config['SUPABASE_ANON_KEY']}"
return httpx.Client(base_url=base, headers=headers, auth=auth, timeout=10)
def pg_unique(col: str) -> List[str]:
with build_pg_client() as c:
r = c.get("/Fault_Code_Table", params={"select": col})
r.raise_for_status()
vals = [row.get(col) for row in (r.json() or []) if row.get(col)]
seen: Dict[str, bool] = {}
out: List[str] = []
for v in vals:
if v not in seen:
seen[v] = True
out.append(v)
return out
def pg_unique_from(table: str, col: str) -> List[str]:
"""지정 테이블에서 고유값 리스트를 반환한다."""
with build_pg_client() as c:
r = c.get(f"/{table}", params={"select": col})
r.raise_for_status()
vals = [row.get(col) for row in (r.json() or []) if row.get(col)]
seen: Dict[str, bool] = {}
out: List[str] = []
for v in vals:
if v not in seen:
seen[v] = True
out.append(v)
return out
@app.route("/")
def index():
return render_template(
"index.html",
app_name=APP_NAME,
)
# 기존 SQLite 기반 라우트 제거됨
@app.route("/modal/close")
def modal_close():
return ""
@app.route("/health")
def health():
return {"status": "ok"}
# 빈 파비콘 응답으로 404 제거
@app.route("/favicon.ico")
def favicon():
return ("", 204, {"Content-Type": "image/x-icon"})
# 브라우저/툴 호환을 위해 루트 경로에도 매니페스트 노출 (정적 파일 경로 사용 권장)
# 더 이상 /api/* 동기화 엔드포인트 제공하지 않음
# ----------------- Supabase 기반 라우트 -----------------
@app.route("/sb/tabs")
def sb_tabs():
try:
with build_pg_client() as c:
r1 = c.get("/Fault_Code_Table", params={"select": "manufacturer"})
r1.raise_for_status()
vals1 = [row.get("manufacturer") for row in (r1.json() or []) if row.get("manufacturer")]
vals2: List[str] = []
for path in ("/Signals", "/signals"):
try:
r2 = c.get(path, params={"select": "manufacturer"})
r2.raise_for_status()
vals2 = [row.get("manufacturer") for row in (r2.json() or []) if row.get("manufacturer")]
break
except httpx.HTTPError:
continue
vals = vals1 + vals2
seen: Dict[str, bool] = {}
manufacturers: List[str] = []
for v in vals:
if v not in seen:
seen[v] = True
manufacturers.append(v)
return render_template("partials/sb_tabs.html", manufacturers=manufacturers)
except Exception as e:
return render_template(
"partials/sb_error.html",
error_message=str(e),
supabase_url=app.config.get("SUPABASE_URL"),
)
@app.route("/sb")
def sb_home():
try:
# ---- MMI 코드 분기 ----
if request.args.get("section", "fault").strip() == "mmicode":
PAGE_SIZE = 50
page = int(request.args.get("page", "0"))
offset = page * PAGE_SIZE
# 셀렉트박스/검색 필드
with build_pg_client() as c:
# 제조사
res = c.get("/MMI_Code", params={"select": "manufacturer"})
res.raise_for_status()
manufacturers = sorted({row.get("manufacturer") for row in (res.json() or []) if row.get("manufacturer")})
# 차량분류(alias_name)
res2 = c.get("/MMI_Code", params={"select": "alias_name"})
res2.raise_for_status()
alias_names = sorted({row.get("alias_name") for row in (res2.json() or []) if row.get("alias_name")})
selected_manufacturer = request.args.get("manufacturer", "").strip()
selected_alias_name = request.args.get("alias_name", "").strip()
q = request.args.get("q", "").strip()
group_code = request.args.get("group_code", "").strip().lower() == "on"
# MMI 쿼리
params = {
"select": "id,code_name,code_description,data_type,car_id,alias_name,manufacturer",
"order": "code_name.asc",
"limit": str(PAGE_SIZE),
"offset": str(offset),
}
if selected_manufacturer:
params["manufacturer"] = f"eq.{selected_manufacturer}"
if selected_alias_name:
params["alias_name"] = f"eq.{selected_alias_name}"
if q:
# 여러 컬럼에 대해 ilike filter
params["or"] = f"(code_name.ilike.*{q}*,code_description.ilike.*{q}*,alias_name.ilike.*{q}*)"
with build_pg_client() as c:
res = c.get("/MMI_Code", params=params)
res.raise_for_status()
rows = res.json() or []
# dedup if group_code on
if group_code:
seen = set()
dedup = []
for r in rows:
code = r.get("code_name")
if code and code not in seen:
seen.add(code)
dedup.append(r)
rows = dedup
query_params = request.args.to_dict()
if "page" in query_params:
del query_params["page"]
query_params_string = urlencode(query_params)
# 템플릿 렌더 및 선택 파라미터 전달
return render_template(
"partials/sb_mmi_list.html",
rows=rows,
manufacturers=manufacturers,
alias_names=alias_names,
selected_manufacturer=selected_manufacturer,
selected_alias_name=selected_alias_name,
q=q,
group_code="on" if group_code else "off",
section="mmicode",
page=page,
page_size=PAGE_SIZE,
query_params_string=query_params_string,
)
# ---- 기존(고장코드/TCMS) ----
mf_fault = pg_unique("manufacturer")
mf_signal = pg_unique_from("Signals", "manufacturer")
seen_mf: Dict[str, bool] = {}
manufacturers: List[str] = []
for v in (mf_fault + mf_signal):
if v and v not in seen_mf:
seen_mf[v] = True
manufacturers.append(v)
devices = pg_unique("device")
car_types = pg_unique("car_type")
car_ids = pg_unique("car_id")
# alias_name 필터는 제작사에 따라 값 제한: 우진/로템/다대 포함
all_alias = pg_unique("alias_name")
selected_manufacturer = request.args.get("manufacturer", "").strip()
def alias_allowed(name: str) -> bool:
if not name:
return False
low = name.lower()
if selected_manufacturer.lower() == "woojin":
return ("우진" in name) or ("woojin" in low)
if selected_manufacturer.lower() == "rotem":
return ("로템" in name) or ("다대" in name) or ("rotem" in low)
return True
alias_names = [a for a in all_alias if alias_allowed(a)]
signal_classifications = pg_unique_from("Signals", "classification")
# 선택값 처리 (쿼리스트링에 manufacturer 키가 있으면 빈 문자열이라도 그대로 유지)
if "manufacturer" in request.args:
selected_manufacturer = (request.args.get("manufacturer") or "").strip()
else:
selected_manufacturer = manufacturers[0] if manufacturers else ""
# 다른 필터의 현재 선택값 유지
selected_device = request.args.get("device", "").strip()
selected_car_type = request.args.get("car_type", "").strip()
selected_alias_name = request.args.get("alias_name", "").strip()
selected_classification = request.args.get("classification", "").strip()
q = request.args.get("q", "").strip()
section = request.args.get("section", "fault").strip() or "fault"
return render_template(
"partials/sb_manufacturer.html",
manufacturers=manufacturers,
devices=devices,
car_types=car_types,
car_ids=car_ids,
alias_names=alias_names,
signal_classifications=signal_classifications,
selected_manufacturer=selected_manufacturer,
selected_device=selected_device,
selected_car_type=selected_car_type,
selected_alias_name=selected_alias_name,
selected_classification=selected_classification,
q=q,
section=section,
)
except Exception as e:
return render_template(
"partials/sb_error.html",
error_message=str(e),
supabase_url=app.config.get("SUPABASE_URL"),
)
@app.route("/sb/faults/list")
def sb_faults_list():
try:
PAGE_SIZE = 50
page = int(request.args.get("page", "0"))
offset = page * PAGE_SIZE
manufacturer = request.args.get("manufacturer", "").strip()
device = request.args.get("device", "").strip()
car_type = request.args.get("car_type", "").strip()
car_id = request.args.get("car_id", "").strip()
alias_name = request.args.get("alias_name", "").strip()
q = request.args.get("q", "").strip()
section = request.args.get("section", "fault").strip()
params: Dict[str, str] = {
"select": "f_code,f_code_num,f_name,manufacturer,device,car_type,car_id,fault_detail,alias_name",
"order": "f_code.asc",
"limit": str(PAGE_SIZE),
"offset": str(offset),
}
if manufacturer:
params["manufacturer"] = f"eq.{manufacturer}"
if device:
params["device"] = f"eq.{device}"
if car_type:
params["car_type"] = f"eq.{car_type}"
if car_id:
params["car_id"] = f"eq.{car_id}"
if alias_name:
params["alias_name"] = f"eq.{alias_name}"
if q:
params["or"] = f"(f_code.ilike.*{q}*,f_name.ilike.*{q}*)"
with build_pg_client() as c:
r = c.get("/Fault_Code_Table", params=params)
r.raise_for_status()
rows = r.json() or []
# 그룹핑 옵션: 같은 f_code를 첫 항목만 남김
group_code = request.args.get("group_code", "").strip().lower()
if group_code == "on":
seen: Dict[str, bool] = {}
dedup: List[Dict] = []
for r in rows:
code = r.get("f_code")
if code and code not in seen:
seen[code] = True
dedup.append(r)
rows = dedup
query_params = request.args.to_dict()
if "page" in query_params:
del query_params["page"]
query_params_string = urlencode(query_params)
return render_template(
"partials/sb_fault_list.html",
rows=rows,
page=page,
page_size=PAGE_SIZE,
query_params_string=query_params_string,
)
except Exception as e:
return render_template(
"partials/sb_error.html",
error_message=str(e),
supabase_url=app.config.get("SUPABASE_URL"),
)
@app.route("/sb/faults/<string:f_code>")
def sb_fault_detail(f_code: str):
try:
params = {
"select": "f_code,f_code_num,f_name,car_type,f_class,grade,device,fault_detail,fault_reaction,fault_detection,fault_clear,fault_action,fault_schematics,car_id,manufacturer",
"f_code": f"eq.{f_code}",
"limit": "1",
}
with build_pg_client() as c:
r = c.get("/Fault_Code_Table", params=params)
r.raise_for_status()
rows = r.json() or []
if not rows:
abort(404)
row = rows[0]
return render_template("partials/sb_fault_detail.html", row=row)
except Exception as e:
return render_template(
"partials/sb_error.html",
error_message=str(e),
supabase_url=app.config.get("SUPABASE_URL"),
)
# ----------------- Signals (TCMS) -----------------
@app.route("/sb/signals/list")
def sb_signals_list():
try:
PAGE_SIZE = 50
page = int(request.args.get("page", "0"))
offset = page * PAGE_SIZE
manufacturer = request.args.get("manufacturer", "").strip()
classification = request.args.get("classification", "").strip()
q = request.args.get("q", "").strip()
alias_name = request.args.get("alias_name", "").strip()
params: Dict[str, str] = {
"select": "id,sig_num,signal_abbreviation,signal_description,status_value,manufacturer,classification,alias_name",
"order": "sig_num.asc",
"limit": str(PAGE_SIZE),
"offset": str(offset),
}
if manufacturer:
params["manufacturer"] = f"eq.{manufacturer}"
if classification:
params["classification"] = f"eq.{classification}"
if alias_name:
params["alias_name"] = f"eq.{alias_name}"
if q:
params["or"] = f"(signal_abbreviation.ilike.*{q}*,signal_description.ilike.*{q}*)"
with build_pg_client() as c:
rows = []
last_error: Exception | None = None
for path in ("/Signals", "/signals"):
try:
r = c.get(path, params=params)
r.raise_for_status()
rows = r.json() or []
last_error = None
break
except Exception as e:
last_error = e
rows = []
continue
if last_error and not rows:
raise last_error
# TCMS도 group_code=on이면 sig_num/dedup(또는 id?)
group_code = request.args.get("group_code", "").strip().lower()
if group_code == "on":
seen: Dict[str, bool] = {}
dedup: List[Dict] = []
for r in rows:
code = r.get("id")
if code and code not in seen:
seen[code] = True
dedup.append(r)
rows = dedup
query_params = request.args.to_dict()
if "page" in query_params:
del query_params["page"]
query_params_string = urlencode(query_params)
return render_template(
"partials/sb_signal_list.html",
rows=rows,
page=page,
page_size=PAGE_SIZE,
query_params_string=query_params_string,
)
except Exception as e:
return render_template(
"partials/sb_error.html",
error_message=str(e),
supabase_url=app.config.get("SUPABASE_URL"),
)
@app.route("/sb/signals/<string:item_id>")
def sb_signal_detail(item_id: str):
try:
params = {
"select": "id,sig_num,signal_abbreviation,signal_description,status_value,manufacturer,classification,alias_name,original_data,created_at,updated_at",
"id": f"eq.{item_id}",
"limit": "1",
}
with build_pg_client() as c:
rows = []
last_error: Exception | None = None
for path in ("/Signals", "/signals"):
try:
r = c.get(path, params=params)
r.raise_for_status()
rows = r.json() or []
last_error = None
break
except Exception as e:
last_error = e
rows = []
continue
if last_error and not rows:
raise last_error
if not rows:
abort(404)
row = rows[0]
return render_template("partials/sb_signal_detail.html", row=row)
except Exception as e:
return render_template(
"partials/sb_error.html",
error_message=str(e),
supabase_url=app.config.get("SUPABASE_URL"),
)
@app.route("/sb/health")
def sb_health():
try:
with build_pg_client() as c:
r = c.get("/Fault_Code_Table", params={"select": "f_code", "limit": "1"})
r.raise_for_status()
return {"sb": "ok", "url": app.config.get("SUPABASE_URL")}
except Exception as e:
return {"sb": "error", "url": app.config.get("SUPABASE_URL"), "error": str(e)}
# 간단한 Signals 테스트 엔드포인트: 상위 5개 레코드를 JSON으로 반환
@app.route("/sb/signals/test")
def sb_signals_test():
try:
params = {
"select": "uuid,sig_num,signal_abbreviation,manufacturer,classification,alias_name",
"order": "sig_num.asc",
"limit": "5",
}
with build_pg_client() as c:
rows = []
used_path = None
last_error = None
for path in ("/Signals", "/signals"):
try:
r = c.get(path, params=params)
r.raise_for_status()
rows = r.json() or []
used_path = path
last_error = None
break
except Exception as e:
last_error = str(e)
rows = []
continue
if last_error and not rows:
return {"ok": False, "url": app.config.get("SUPABASE_URL"), "tried": ["/Signals", "/signals"], "error": last_error}, 500
return {"ok": True, "url": app.config.get("SUPABASE_URL"), "path": used_path, "rows": rows}
except Exception as e:
return {"ok": False, "url": app.config.get("SUPABASE_URL"), "error": str(e)}, 500
# 상세 디버그: 두 경로 각각의 상태/본문을 그대로 반환
@app.route("/sb/signals/debug")
def sb_signals_debug():
try:
out = []
with build_pg_client() as c:
for path in ("/Signals", "/signals"):
try:
r = c.get(path, params={"select": "*", "limit": "5"})
ct = r.headers.get("content-type", "")
body = None
try:
body = r.json()
except Exception:
body = r.text
out.append({
"path": path,
"status": r.status_code,
"content_type": ct,
"body": body,
})
except httpx.HTTPError as e:
out.append({"path": path, "error": str(e)})
return {"ok": True, "url": app.config.get("SUPABASE_URL"), "results": out}
except Exception as e:
return {"ok": False, "url": app.config.get("SUPABASE_URL"), "error": str(e)}, 500
return app
app = create_app()
if __name__ == "__main__":
port = int(os.environ.get("PORT", "5000"))
app.run(host="0.0.0.0", port=port, debug=True)

215
app/build.gradle Normal file
View File

@ -0,0 +1,215 @@
/*
* Copyright 2019 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import groovy.xml.MarkupBuilder
plugins {
id 'com.android.application'
}
def twaManifest = [
applicationId: 'cc.m1tcloud.tr.twa',
hostName: 'tr.m1tcloud.cc', // The domain being opened in the TWA.
launchUrl: '/', // The start path for the TWA. Must be relative to the domain.
name: 'FaultCode_Line1', // The application name.
launcherName: 'F_Code', // The name shown on the Android Launcher.
themeColor: '#0F172A', // The color used for the status bar.
themeColorDark: '#000000', // The color used for the dark status bar.
navigationColor: '#000000', // The color used for the navigation bar.
navigationColorDark: '#000000', // The color used for the dark navbar.
navigationDividerColor: '#000000', // The navbar divider color.
navigationDividerColorDark: '#000000', // The dark navbar divider color.
backgroundColor: '#0F172A', // The color used for the splash screen background.
enableNotifications: true, // Set to true to enable notification delegation.
// Every shortcut must include the following fields:
// - name: String that will show up in the shortcut.
// - short_name: Shorter string used if |name| is too long.
// - url: Absolute path of the URL to launch the app with (e.g '/create').
// - icon: Name of the resource in the drawable folder to use as an icon.
shortcuts: [],
// The duration of fade out animation in milliseconds to be played when removing splash screen.
splashScreenFadeOutDuration: 300,
generatorApp: 'bubblewrap-cli', // Application that generated the Android Project
// The fallback strategy for when Trusted Web Activity is not available. Possible values are
// 'customtabs' and 'webview'.
fallbackType: 'customtabs',
enableSiteSettingsShortcut: 'true',
orientation: 'default',
]
android {
compileSdkVersion 36
namespace "cc.m1tcloud.tr.twa"
defaultConfig {
applicationId "cc.m1tcloud.tr.twa"
minSdkVersion 21
targetSdkVersion 35
versionCode 1
versionName "1"
// The name for the application
resValue "string", "appName", twaManifest.name
// The name for the application on the Android Launcher
resValue "string", "launcherName", twaManifest.launcherName
// The URL that will be used when launching the TWA from the Android Launcher
def launchUrl = "https://" + twaManifest.hostName + twaManifest.launchUrl
resValue "string", "launchUrl", launchUrl
// The URL the Web Manifest for the Progressive Web App that the TWA points to. This
// is used by Chrome OS and Meta Quest to open the Web version of the PWA instead of
// the TWA, as it will probably give a better user experience for non-mobile devices.
resValue "string", "webManifestUrl", 'https://tr.m1tcloud.cc/manifest.webmanifest'
// This is used by Meta Quest.
resValue "string", "fullScopeUrl", 'https://tr.m1tcloud.cc/'
// The hostname is used when building the intent-filter, so the TWA is able to
// handle Intents to open host url of the application.
resValue "string", "hostName", twaManifest.hostName
// This attribute sets the status bar color for the TWA. It can be either set here or in
// `res/values/colors.xml`. Setting in both places is an error and the app will not
// compile. If not set, the status bar color defaults to #FFFFFF - white.
resValue "color", "colorPrimary", twaManifest.themeColor
// This attribute sets the dark status bar color for the TWA. It can be either set here or in
// `res/values/colors.xml`. Setting in both places is an error and the app will not
// compile. If not set, the status bar color defaults to #000000 - white.
resValue "color", "colorPrimaryDark", twaManifest.themeColorDark
// This attribute sets the navigation bar color for the TWA. It can be either set here or
// in `res/values/colors.xml`. Setting in both places is an error and the app will not
// compile. If not set, the navigation bar color defaults to #FFFFFF - white.
resValue "color", "navigationColor", twaManifest.navigationColor
// This attribute sets the dark navigation bar color for the TWA. It can be either set here
// or in `res/values/colors.xml`. Setting in both places is an error and the app will not
// compile. If not set, the navigation bar color defaults to #000000 - black.
resValue "color", "navigationColorDark", twaManifest.navigationColorDark
// This attribute sets the navbar divider color for the TWA. It can be either
// set here or in `res/values/colors.xml`. Setting in both places is an error and the app
// will not compile. If not set, the divider color defaults to #00000000 - transparent.
resValue "color", "navigationDividerColor", twaManifest.navigationDividerColor
// This attribute sets the dark navbar divider color for the TWA. It can be either
// set here or in `res/values/colors.xml`. Setting in both places is an error and the
//app will not compile. If not set, the divider color defaults to #000000 - black.
resValue "color", "navigationDividerColorDark", twaManifest.navigationDividerColorDark
// Sets the color for the background used for the splash screen when launching the
// Trusted Web Activity.
resValue "color", "backgroundColor", twaManifest.backgroundColor
// Defines a provider authority for the Splash Screen
resValue "string", "providerAuthority", twaManifest.applicationId + '.fileprovider'
// The enableNotification resource is used to enable or disable the
// TrustedWebActivityService, by changing the android:enabled and android:exported
// attributes
resValue "bool", "enableNotification", twaManifest.enableNotifications.toString()
twaManifest.shortcuts.eachWithIndex { shortcut, index ->
resValue "string", "shortcut_name_$index", "$shortcut.name"
resValue "string", "shortcut_short_name_$index", "$shortcut.short_name"
}
// The splashScreenFadeOutDuration resource is used to set the duration of fade out animation in milliseconds
// to be played when removing splash screen. The default is 0 (no animation).
resValue "integer", "splashScreenFadeOutDuration", twaManifest.splashScreenFadeOutDuration.toString()
resValue "string", "generatorApp", twaManifest.generatorApp
resValue "string", "fallbackType", twaManifest.fallbackType
resValue "bool", "enableSiteSettingsShortcut", twaManifest.enableSiteSettingsShortcut
resValue "string", "orientation", twaManifest.orientation
}
buildTypes {
release {
minifyEnabled true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
lintOptions {
checkReleaseBuilds false
}
}
task generateShorcutsFile {
assert twaManifest.shortcuts.size() < 5, "You can have at most 4 shortcuts."
twaManifest.shortcuts.eachWithIndex { s, i ->
assert s.name != null, 'Missing `name` in shortcut #' + i
assert s.short_name != null, 'Missing `short_name` in shortcut #' + i
assert s.url != null, 'Missing `icon` in shortcut #' + i
assert s.icon != null, 'Missing `url` in shortcut #' + i
}
def shortcutsFile = new File("$projectDir/src/main/res/xml", "shortcuts.xml")
def xmlWriter = new StringWriter()
def xmlMarkup = new MarkupBuilder(new IndentPrinter(xmlWriter, " ", true))
xmlMarkup
.'shortcuts'('xmlns:android': 'http://schemas.android.com/apk/res/android') {
twaManifest.shortcuts.eachWithIndex { s, i ->
'shortcut'(
'android:shortcutId': 'shortcut' + i,
'android:enabled': 'true',
'android:icon': '@drawable/' + s.icon,
'android:shortcutShortLabel': '@string/shortcut_short_name_' + i,
'android:shortcutLongLabel': '@string/shortcut_name_' + i) {
'intent'(
'android:action': 'android.intent.action.MAIN',
'android:targetPackage': twaManifest.applicationId,
'android:targetClass': twaManifest.applicationId + '.LauncherActivity',
'android:data': s.url)
'categories'('android:name': 'android.intent.category.LAUNCHER')
}
}
}
shortcutsFile.text = xmlWriter.toString() + '\n'
}
preBuild.dependsOn(generateShorcutsFile)
repositories {
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.google.androidbrowserhelper:locationdelegation:1.1.2'
implementation 'com.google.androidbrowserhelper:androidbrowserhelper:2.6.2'
}

View File

@ -0,0 +1,203 @@
<!--
Copyright 2019 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- The "package" attribute is rewritten by the Gradle build with the value of applicationId.
It is still required here, as it is used to derive paths, for instance when referring
to an Activity by ".MyActivity" instead of the full name. If more Activities are added to the
application, the package attribute will need to reflect the correct path in order to use
the abbreviated format. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cc.m1tcloud.tr.twa">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application
android:name="Application"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/appName"
android:manageSpaceActivity="com.google.androidbrowserhelper.trusted.ManageDataLauncherActivity"
android:supportsRtl="true"
android:theme="@android:style/Theme.Translucent.NoTitleBar">
<meta-data
android:name="asset_statements"
android:resource="@string/assetStatements" />
<meta-data
android:name="web_manifest_url"
android:value="@string/webManifestUrl" />
<meta-data
android:name="twa_generator"
android:value="@string/generatorApp" />
<activity android:name="com.google.androidbrowserhelper.trusted.ManageDataLauncherActivity"
android:exported="false"
android:enabled="true"
android:excludeFromRecents="true">
<meta-data
android:name="android.support.customtabs.trusted.MANAGE_SPACE_URL"
android:value="@string/launchUrl" />
<intent-filter>
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name="LauncherActivity"
android:alwaysRetainTaskState="true"
android:label="@string/launcherName"
android:exported="true">
<meta-data android:name="android.support.customtabs.trusted.DEFAULT_URL"
android:value="@string/launchUrl" />
<meta-data
android:name="android.support.customtabs.trusted.STATUS_BAR_COLOR"
android:resource="@color/colorPrimary" />
<meta-data
android:name="android.support.customtabs.trusted.STATUS_BAR_COLOR_DARK"
android:resource="@color/colorPrimaryDark" />
<meta-data
android:name="android.support.customtabs.trusted.NAVIGATION_BAR_COLOR"
android:resource="@color/navigationColor" />
<meta-data
android:name="android.support.customtabs.trusted.NAVIGATION_BAR_COLOR_DARK"
android:resource="@color/navigationColorDark" />
<meta-data
android:name="androix.browser.trusted.NAVIGATION_BAR_DIVIDER_COLOR"
android:resource="@color/navigationDividerColor" />
<meta-data
android:name="androix.browser.trusted.NAVIGATION_BAR_DIVIDER_COLOR_DARK"
android:resource="@color/navigationDividerColorDark" />
<meta-data android:name="android.support.customtabs.trusted.SPLASH_IMAGE_DRAWABLE"
android:resource="@drawable/splash"/>
<meta-data android:name="android.support.customtabs.trusted.SPLASH_SCREEN_BACKGROUND_COLOR"
android:resource="@color/backgroundColor"/>
<meta-data android:name="android.support.customtabs.trusted.SPLASH_SCREEN_FADE_OUT_DURATION"
android:value="@integer/splashScreenFadeOutDuration"/>
<meta-data android:name="android.support.customtabs.trusted.FILE_PROVIDER_AUTHORITY"
android:value="@string/providerAuthority"/>
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />
<meta-data android:name="android.support.customtabs.trusted.FALLBACK_STRATEGY"
android:value="@string/fallbackType" />
<meta-data android:name="android.support.customtabs.trusted.SCREEN_ORIENTATION"
android:value="@string/orientation"/>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https"
android:host="@string/hostName"
/>
</intent-filter>
</activity>
<activity android:name="com.google.androidbrowserhelper.trusted.FocusActivity" />
<activity android:name="com.google.androidbrowserhelper.trusted.WebViewFallbackActivity"
android:configChanges="orientation|screenSize" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="@string/providerAuthority"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
<service
android:name=".DelegationService"
android:enabled="@bool/enableNotification"
android:exported="@bool/enableNotification">
<meta-data
android:name="android.support.customtabs.trusted.SMALL_ICON"
android:resource="@drawable/ic_notification_icon" />
<intent-filter>
<action android:name="android.support.customtabs.trusted.TRUSTED_WEB_ACTIVITY_SERVICE"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
<activity android:name="com.google.androidbrowserhelper.trusted.NotificationPermissionRequestActivity" />
<activity android:name=
"com.google.androidbrowserhelper.locationdelegation.PermissionRequestActivity"/>
</application>
</manifest>

View File

@ -0,0 +1,29 @@
/*
* Copyright 2020 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cc.m1tcloud.tr.twa;
public class Application extends android.app.Application {
@Override
public void onCreate() {
super.onCreate();
}
}

View File

@ -0,0 +1,18 @@
package cc.m1tcloud.tr.twa;
import com.google.androidbrowserhelper.locationdelegation.LocationDelegationExtraCommandHandler;
public class DelegationService extends
com.google.androidbrowserhelper.trusted.DelegationService {
@Override
public void onCreate() {
super.onCreate();
registerExtraCommandHandler(new LocationDelegationExtraCommandHandler());
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2020 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cc.m1tcloud.tr.twa;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
public class LauncherActivity
extends com.google.androidbrowserhelper.trusted.LauncherActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Setting an orientation crashes the app due to the transparent background on Android 8.0
// Oreo and below. We only set the orientation on Oreo and above. This only affects the
// splash screen and Chrome will still respect the orientation.
// See https://github.com/GoogleChromeLabs/bubblewrap/issues/496 for details.
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}
@Override
protected Uri getLaunchingUrl() {
// Get the original launch Url.
Uri uri = super.getLaunchingUrl();
return uri;
}
}

View File

@ -0,0 +1,25 @@
<!--
Copyright 2020 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:inset="2dp">
<aapt:attr name="android:drawable">
<shape android:shape="oval">
<solid android:color="@color/shortcut_background" />
<size android:width="44dp" android:height="44dp" />
</shape>
</aapt:attr>
</inset>

View File

@ -0,0 +1,18 @@
<!--
Copyright 2020 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<color name="shortcut_background">#F5F5F5</color>
</resources>

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2021 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!--
The "fullscreen" and "fullscreen-sticky" TWA manifest values correspond to "immersive" and "sticky-immersive"
AndroidManifest values.
-->
<!--
This variable below expresses the relationship between the app and the site,
as documented in the TWA documentation at
https://developers.google.com/web/updates/2017/10/using-twa#set_up_digital_asset_links_in_an_android_app
and is injected into the AndroidManifest.xml
-->
<string name="assetStatements">
[{
\"relation\": [\"delegate_permission/common.handle_all_urls\"],
\"target\": {
\"namespace\": \"web\",
\"site\": \"https://tr.m1tcloud.cc\"
}
}]
</string>
</resources>

View File

@ -0,0 +1,18 @@
<!--
Copyright 2019 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<paths>
<files-path path="twa_splash/" name="twa_splash" />
</paths>

View File

@ -0,0 +1,16 @@
<!--
Copyright 2019 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<shortcuts xmlns:android='http://schemas.android.com/apk/res/android' />

42
build.gradle Normal file
View File

@ -0,0 +1,42 @@
/*
* Copyright 2019 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.9.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

BIN
fault_codes.db Normal file

Binary file not shown.

14
gradle.properties Normal file
View File

@ -0,0 +1,14 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useAndroidX=true

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true

249
gradlew vendored Normal file
View File

@ -0,0 +1,249 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

92
gradlew.bat vendored Normal file
View File

@ -0,0 +1,92 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 KiB

10
requirements.txt Normal file
View File

@ -0,0 +1,10 @@
Flask==3.0.3
itsdangerous>=2.1
Jinja2>=3.1
Werkzeug>=3.0
htmx
# htmx==1.9.12 ; python_version>='0' # (note) for reference only, loaded via CDN
supabase>=2.4.0
python-dotenv>=1.0.1

11712
rotem_f.csv Normal file

File diff suppressed because it is too large Load Diff

11711
rotem_f2.csv Normal file

File diff suppressed because it is too large Load Diff

1
settings.gradle Normal file
View File

@ -0,0 +1 @@
include ':app'

BIN
static/Icons/icon-192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
static/Icons/icon-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

89
static/app.js Normal file
View File

@ -0,0 +1,89 @@
(function(){
// 테마 자동/토글
const THEME_KEY = 'theme-preference';
const themeToggle = document.getElementById('themeToggle');
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)');
const getPreferredTheme = () => localStorage.getItem(THEME_KEY) || 'auto';
const resolveTheme = (pref) => {
if (pref === 'dark' || pref === 'light') return pref;
return prefersDark && prefersDark.matches ? 'dark' : 'light';
};
const applyTheme = (themePref) => {
const theme = resolveTheme(themePref);
document.documentElement.setAttribute('data-theme', theme);
const meta = document.querySelector('meta[name="theme-color"]');
if (meta) meta.setAttribute('content', theme === 'dark' ? '#0b1220' : '#ffffff');
};
applyTheme(getPreferredTheme());
prefersDark && prefersDark.addEventListener('change', () => applyTheme(getPreferredTheme()));
if (themeToggle) {
themeToggle.addEventListener('click', () => {
const now = getPreferredTheme();
const next = now === 'dark' ? 'light' : now === 'light' ? 'auto' : 'dark';
localStorage.setItem(THEME_KEY, next);
applyTheme(next);
});
}
// htmx modal 지원
document.body.addEventListener('htmx:afterSwap', (e) => {
if (e.target.id === 'modal') {
const dlg = e.target;
if (typeof dlg.showModal === 'function') dlg.showModal();
}
});
document.body.addEventListener('click', (e) => {
const dlg = document.getElementById('modal');
if (dlg && e.target === dlg) {
dlg.close();
dlg.innerHTML = '';
}
});
// 탭 선택 상태 토글 (상단 섹션/서브탭)
document.addEventListener('click', (e) => {
const sectionTabBtn = e.target.closest('#topSections .tab-button');
const topTabBtn = e.target.closest('#tabs .tab-button');
const subTabBtn = e.target.closest('#subtabs .tab-button');
// 상단 섹션 탭: 선택 상태 토글
if (sectionTabBtn) {
document.querySelectorAll('#topSections .tab-button')
.forEach(x => x.setAttribute('aria-selected', 'false'));
sectionTabBtn.setAttribute('aria-selected', 'true');
}
// 상단 제조사 탭: 섹션 값 전달 + 선택 상태 토글
if (topTabBtn) {
const sectionRoot = document.getElementById('currentSectionRoot');
if (sectionRoot) topTabBtn.setAttribute('hx-vals', JSON.stringify({ section: sectionRoot.value }));
document.querySelectorAll('#tabs .tab-button')
.forEach(x => x.setAttribute('aria-selected', 'false'));
topTabBtn.setAttribute('aria-selected', 'true');
}
// 하위 서브탭: 선택 상태 토글
if (subTabBtn) {
document.querySelectorAll('#subtabs .tab-button')
.forEach(x => x.setAttribute('aria-selected', 'false'));
subTabBtn.setAttribute('aria-selected', 'true');
}
});
// PWA 등록
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/static/sw.js').catch(()=>{});
});
}
// 로컬 SQLite / PWA 동기화 제거됨 (Supabase 직접 조회)
})();

98
static/db.js Normal file
View File

@ -0,0 +1,98 @@
// OPFS 기반 WebAssembly SQLite (sql.js) 초기화 및 동기화
// 가벼운 전체 파일 교체 방식. 향후 Supabase 연동 시 증분 동기화로 확장 가능.
const DB = (() => {
let SQL, db, ready;
const OPFS_FILE = 'faults.db';
const STORE_KEY = 'db-version';
async function ensureSqlJs() {
if (SQL) return SQL;
// sql.js CDN
const module = await window.initSqlJs({ locateFile: f => `https://sql.js.org/dist/${f}` });
SQL = module;
return SQL;
}
async function readFromOpfs() {
if (!('storage' in navigator) || !('getDirectory' in navigator.storage)) return null;
try {
const root = await navigator.storage.getDirectory();
const handle = await root.getFileHandle(OPFS_FILE, { create: false }).catch(() => null);
if (!handle) return null;
const file = await handle.getFile();
return new Uint8Array(await file.arrayBuffer());
} catch { return null; }
}
async function writeToOpfs(bytes) {
if (!('storage' in navigator) || !('getDirectory' in navigator.storage)) return;
const root = await navigator.storage.getDirectory();
const handle = await root.getFileHandle(OPFS_FILE, { create: true });
const w = await handle.createWritable();
await w.write(bytes);
await w.close();
}
async function init() {
if (ready) return ready;
ready = (async () => {
await ensureSqlJs();
// 1) OPFS에서 기존 DB 로드
const bytes = await readFromOpfs();
if (bytes) {
db = new SQL.Database(bytes);
} else {
// 없으면 서버에서 부트스트랩 DB 다운로드
const buf = await fetch('/api/db').then(r => r.arrayBuffer());
db = new SQL.Database(new Uint8Array(buf));
await persist();
}
return true;
})();
return ready;
}
async function persist() {
const data = db.export();
await writeToOpfs(data);
}
async function syncIfNeeded() {
// 서버 버전 확인 후 다르면 교체
try {
const remoteMeta = await fetch('/api/meta').then(r => r.json());
const localMeta = getLocalMeta();
if (!localMeta || Number(remoteMeta.version) > Number(localMeta.version)) {
const buf = await fetch('/api/db').then(r => r.arrayBuffer());
db = new SQL.Database(new Uint8Array(buf));
await persist();
setLocalMeta(remoteMeta);
return true;
}
} catch {}
return false;
}
function getLocalMeta() {
try { return JSON.parse(localStorage.getItem(STORE_KEY)); } catch { return null; }
}
function setLocalMeta(meta) {
localStorage.setItem(STORE_KEY, JSON.stringify(meta));
}
function query(sql, params = []) {
const stmt = db.prepare(sql);
stmt.bind(params);
const rows = [];
while (stmt.step()) rows.push(stmt.getAsObject());
stmt.free();
return rows;
}
return { init, syncIfNeeded, query, persist };
})();
window.DB = DB;

View File

@ -0,0 +1,15 @@
{
"name": "1호선 고장코드",
"short_name": "고장코드",
"start_url": "/",
"display": "standalone",
"background_color": "#0f172a",
"theme_color": "#0f172a",
"lang": "ko",
"icons": [
{"src": "/static/icons/icon-192.png", "sizes": "192x192", "type": "image/png"},
{"src": "/static/icons/icon-512.png", "sizes": "512x512", "type": "image/png"}
]
}

192
static/sql-wasm.1.16.js Normal file
View File

@ -0,0 +1,192 @@
// We are modularizing this manually because the current modularize setting in Emscripten has some issues:
// https://github.com/kripken/emscripten/issues/5820
// In addition, When you use emcc's modularization, it still expects to export a global object called `Module`,
// which is able to be used/called before the WASM is loaded.
// The modularization below exports a promise that loads and resolves to the actual sql.js module.
// That way, this module can't be used before the WASM is finished loading.
// We are going to define a function that a user will call to start loading initializing our Sql.js library
// However, that function might be called multiple times, and on subsequent calls, we don't actually want it to instantiate a new instance of the Module
// Instead, we want to return the previously loaded module
// TODO: Make this not declare a global if used in the browser
var initSqlJsPromise = undefined;
var initSqlJs = function (moduleConfig) {
if (initSqlJsPromise){
return initSqlJsPromise;
}
// If we're here, we've never called this function before
initSqlJsPromise = new Promise(function (resolveModule, reject) {
// We are modularizing this manually because the current modularize setting in Emscripten has some issues:
// https://github.com/kripken/emscripten/issues/5820
// The way to affect the loading of emcc compiled modules is to create a variable called `Module` and add
// properties to it, like `preRun`, `postRun`, etc
// We are using that to get notified when the WASM has finished loading.
// Only then will we return our promise
// If they passed in a moduleConfig object, use that
// Otherwise, initialize Module to the empty object
var Module = typeof moduleConfig !== 'undefined' ? moduleConfig : {};
// EMCC only allows for a single onAbort function (not an array of functions)
// So if the user defined their own onAbort function, we remember it and call it
var originalOnAbortFunction = Module['onAbort'];
Module['onAbort'] = function (errorThatCausedAbort) {
reject(new Error(errorThatCausedAbort));
if (originalOnAbortFunction){
originalOnAbortFunction(errorThatCausedAbort);
}
};
Module['postRun'] = Module['postRun'] || [];
Module['postRun'].push(function () {
// When Emscripted calls postRun, this promise resolves with the built Module
resolveModule(Module);
});
// There is a section of code in the emcc-generated code below that looks like this:
// (Note that this is lowercase `module`)
// if (typeof module !== 'undefined') {
// module['exports'] = Module;
// }
// When that runs, it's going to overwrite our own modularization export efforts in shell-post.js!
// The only way to tell emcc not to emit it is to pass the MODULARIZE=1 or MODULARIZE_INSTANCE=1 flags,
// but that carries with it additional unnecessary baggage/bugs we don't want either.
// So, we have three options:
// 1) We undefine `module`
// 2) We remember what `module['exports']` was at the beginning of this function and we restore it later
// 3) We write a script to remove those lines of code as part of the Make process.
//
// Since those are the only lines of code that care about module, we will undefine it. It's the most straightforward
// of the options, and has the side effect of reducing emcc's efforts to modify the module if its output were to change in the future.
// That's a nice side effect since we're handling the modularization efforts ourselves
module = undefined;
// The emcc-generated code and shell-post.js code goes below,
// meaning that all of it runs inside of this promise. If anything throws an exception, our promise will abort
var f;f||=typeof Module !== 'undefined' ? Module : {};"use strict";
f.onRuntimeInitialized=function(){function a(g,l){switch(typeof l){case "boolean":mc(g,l?1:0);break;case "number":nc(g,l);break;case "string":oc(g,l,-1,-1);break;case "object":if(null===l)lb(g);else if(null!=l.length){var n=aa(l,ba);pc(g,n,l.length,-1);ca(n)}else Aa(g,"Wrong API use : tried to return a value of an unknown type ("+l+").",-1);break;default:lb(g)}}function b(g,l){for(var n=[],t=0;t<g;t+=1){var w=m(l+4*t,"i32"),z=qc(w);if(1===z||2===z)w=rc(w);else if(3===z)w=sc(w);else if(4===z){z=w;
w=tc(z);z=uc(z);for(var N=new Uint8Array(w),L=0;L<w;L+=1)N[L]=p[z+L];w=N}else w=null;n.push(w)}return n}function c(g,l){this.La=g;this.db=l;this.Ja=1;this.fb=[]}function d(g,l){this.db=l;l=da(g)+1;this.Ya=ea(l);if(null===this.Ya)throw Error("Unable to allocate memory for the SQL string");fa(g,q,this.Ya,l);this.eb=this.Ya;this.Ua=this.ib=null}function e(g){this.filename="dbfile_"+(4294967295*Math.random()>>>0);if(null!=g){var l=this.filename,n="/",t=l;n&&(n="string"==typeof n?n:ha(n),t=l?u(n+"/"+l):
n);l=ia(!0,!0);t=ja(t,(void 0!==l?l:438)&4095|32768,0);if(g){if("string"==typeof g){n=Array(g.length);for(var w=0,z=g.length;w<z;++w)n[w]=g.charCodeAt(w);g=n}ka(t,l|146);n=la(t,577);ma(n,g,0,g.length,0);na(n);ka(t,l)}}this.handleError(r(this.filename,h));this.db=m(h,"i32");ob(this.db);this.Za={};this.Na={}}var h=x(4),k=f.cwrap,r=k("sqlite3_open","number",["string","number"]),y=k("sqlite3_close_v2","number",["number"]),v=k("sqlite3_exec","number",["number","string","number","number","number"]),F=k("sqlite3_changes",
"number",["number"]),H=k("sqlite3_prepare_v2","number",["number","string","number","number","number"]),pb=k("sqlite3_sql","string",["number"]),vc=k("sqlite3_normalized_sql","string",["number"]),qb=k("sqlite3_prepare_v2","number",["number","number","number","number","number"]),wc=k("sqlite3_bind_text","number",["number","number","number","number","number"]),rb=k("sqlite3_bind_blob","number",["number","number","number","number","number"]),xc=k("sqlite3_bind_double","number",["number","number","number"]),
yc=k("sqlite3_bind_int","number",["number","number","number"]),zc=k("sqlite3_bind_parameter_index","number",["number","string"]),Ac=k("sqlite3_step","number",["number"]),Bc=k("sqlite3_errmsg","string",["number"]),Cc=k("sqlite3_column_count","number",["number"]),Dc=k("sqlite3_data_count","number",["number"]),Ec=k("sqlite3_column_double","number",["number","number"]),sb=k("sqlite3_column_text","string",["number","number"]),Fc=k("sqlite3_column_blob","number",["number","number"]),Gc=k("sqlite3_column_bytes",
"number",["number","number"]),Hc=k("sqlite3_column_type","number",["number","number"]),Ic=k("sqlite3_column_name","string",["number","number"]),Jc=k("sqlite3_reset","number",["number"]),Kc=k("sqlite3_clear_bindings","number",["number"]),Lc=k("sqlite3_finalize","number",["number"]),tb=k("sqlite3_create_function_v2","number","number string number number number number number number number".split(" ")),qc=k("sqlite3_value_type","number",["number"]),tc=k("sqlite3_value_bytes","number",["number"]),sc=k("sqlite3_value_text",
"string",["number"]),uc=k("sqlite3_value_blob","number",["number"]),rc=k("sqlite3_value_double","number",["number"]),nc=k("sqlite3_result_double","",["number","number"]),lb=k("sqlite3_result_null","",["number"]),oc=k("sqlite3_result_text","",["number","string","number","number"]),pc=k("sqlite3_result_blob","",["number","number","number","number"]),mc=k("sqlite3_result_int","",["number","number"]),Aa=k("sqlite3_result_error","",["number","string","number"]),ub=k("sqlite3_aggregate_context","number",
["number","number"]),ob=k("RegisterExtensionFunctions","number",["number"]);c.prototype.bind=function(g){if(!this.La)throw"Statement closed";this.reset();return Array.isArray(g)?this.wb(g):null!=g&&"object"===typeof g?this.xb(g):!0};c.prototype.step=function(){if(!this.La)throw"Statement closed";this.Ja=1;var g=Ac(this.La);switch(g){case 100:return!0;case 101:return!1;default:throw this.db.handleError(g);}};c.prototype.rb=function(g){null==g&&(g=this.Ja,this.Ja+=1);return Ec(this.La,g)};c.prototype.Ab=
function(g){null==g&&(g=this.Ja,this.Ja+=1);g=sb(this.La,g);if("function"!==typeof BigInt)throw Error("BigInt is not supported");return BigInt(g)};c.prototype.Bb=function(g){null==g&&(g=this.Ja,this.Ja+=1);return sb(this.La,g)};c.prototype.getBlob=function(g){null==g&&(g=this.Ja,this.Ja+=1);var l=Gc(this.La,g);g=Fc(this.La,g);for(var n=new Uint8Array(l),t=0;t<l;t+=1)n[t]=p[g+t];return n};c.prototype.get=function(g,l){l=l||{};null!=g&&this.bind(g)&&this.step();g=[];for(var n=Dc(this.La),t=0;t<n;t+=
1)switch(Hc(this.La,t)){case 1:var w=l.useBigInt?this.Ab(t):this.rb(t);g.push(w);break;case 2:g.push(this.rb(t));break;case 3:g.push(this.Bb(t));break;case 4:g.push(this.getBlob(t));break;default:g.push(null)}return g};c.prototype.getColumnNames=function(){for(var g=[],l=Cc(this.La),n=0;n<l;n+=1)g.push(Ic(this.La,n));return g};c.prototype.getAsObject=function(g,l){g=this.get(g,l);l=this.getColumnNames();for(var n={},t=0;t<l.length;t+=1)n[l[t]]=g[t];return n};c.prototype.getSQL=function(){return pb(this.La)};
c.prototype.getNormalizedSQL=function(){return vc(this.La)};c.prototype.run=function(g){null!=g&&this.bind(g);this.step();return this.reset()};c.prototype.nb=function(g,l){null==l&&(l=this.Ja,this.Ja+=1);g=oa(g);var n=aa(g,ba);this.fb.push(n);this.db.handleError(wc(this.La,l,n,g.length-1,0))};c.prototype.vb=function(g,l){null==l&&(l=this.Ja,this.Ja+=1);var n=aa(g,ba);this.fb.push(n);this.db.handleError(rb(this.La,l,n,g.length,0))};c.prototype.mb=function(g,l){null==l&&(l=this.Ja,this.Ja+=1);this.db.handleError((g===
(g|0)?yc:xc)(this.La,l,g))};c.prototype.yb=function(g){null==g&&(g=this.Ja,this.Ja+=1);rb(this.La,g,0,0,0)};c.prototype.ob=function(g,l){null==l&&(l=this.Ja,this.Ja+=1);switch(typeof g){case "string":this.nb(g,l);return;case "number":this.mb(g,l);return;case "bigint":this.nb(g.toString(),l);return;case "boolean":this.mb(g+0,l);return;case "object":if(null===g){this.yb(l);return}if(null!=g.length){this.vb(g,l);return}}throw"Wrong API use : tried to bind a value of an unknown type ("+g+").";};c.prototype.xb=
function(g){var l=this;Object.keys(g).forEach(function(n){var t=zc(l.La,n);0!==t&&l.ob(g[n],t)});return!0};c.prototype.wb=function(g){for(var l=0;l<g.length;l+=1)this.ob(g[l],l+1);return!0};c.prototype.reset=function(){this.freemem();return 0===Kc(this.La)&&0===Jc(this.La)};c.prototype.freemem=function(){for(var g;void 0!==(g=this.fb.pop());)ca(g)};c.prototype.free=function(){this.freemem();var g=0===Lc(this.La);delete this.db.Za[this.La];this.La=0;return g};d.prototype.next=function(){if(null===
this.Ya)return{done:!0};null!==this.Ua&&(this.Ua.free(),this.Ua=null);if(!this.db.db)throw this.gb(),Error("Database closed");var g=pa(),l=x(4);qa(h);qa(l);try{this.db.handleError(qb(this.db.db,this.eb,-1,h,l));this.eb=m(l,"i32");var n=m(h,"i32");if(0===n)return this.gb(),{done:!0};this.Ua=new c(n,this.db);this.db.Za[n]=this.Ua;return{value:this.Ua,done:!1}}catch(t){throw this.ib=ra(this.eb),this.gb(),t;}finally{sa(g)}};d.prototype.gb=function(){ca(this.Ya);this.Ya=null};d.prototype.getRemainingSQL=
function(){return null!==this.ib?this.ib:ra(this.eb)};"function"===typeof Symbol&&"symbol"===typeof Symbol.iterator&&(d.prototype[Symbol.iterator]=function(){return this});e.prototype.run=function(g,l){if(!this.db)throw"Database closed";if(l){g=this.prepare(g,l);try{g.step()}finally{g.free()}}else this.handleError(v(this.db,g,0,0,h));return this};e.prototype.exec=function(g,l,n){if(!this.db)throw"Database closed";var t=pa(),w=null;try{var z=ta(g),N=x(4);for(g=[];0!==m(z,"i8");){qa(h);qa(N);this.handleError(qb(this.db,
z,-1,h,N));var L=m(h,"i32");z=m(N,"i32");if(0!==L){var K=null;w=new c(L,this);for(null!=l&&w.bind(l);w.step();)null===K&&(K={columns:w.getColumnNames(),values:[]},g.push(K)),K.values.push(w.get(null,n));w.free()}}return g}catch(O){throw w&&w.free(),O;}finally{sa(t)}};e.prototype.each=function(g,l,n,t,w){"function"===typeof l&&(t=n,n=l,l=void 0);g=this.prepare(g,l);try{for(;g.step();)n(g.getAsObject(null,w))}finally{g.free()}if("function"===typeof t)return t()};e.prototype.prepare=function(g,l){qa(h);
this.handleError(H(this.db,g,-1,h,0));g=m(h,"i32");if(0===g)throw"Nothing to prepare";var n=new c(g,this);null!=l&&n.bind(l);return this.Za[g]=n};e.prototype.iterateStatements=function(g){return new d(g,this)};e.prototype["export"]=function(){Object.values(this.Za).forEach(function(l){l.free()});Object.values(this.Na).forEach(ua);this.Na={};this.handleError(y(this.db));var g=va(this.filename);this.handleError(r(this.filename,h));this.db=m(h,"i32");ob(this.db);return g};e.prototype.close=function(){null!==
this.db&&(Object.values(this.Za).forEach(function(g){g.free()}),Object.values(this.Na).forEach(ua),this.Na={},this.handleError(y(this.db)),wa("/"+this.filename),this.db=null)};e.prototype.handleError=function(g){if(0===g)return null;g=Bc(this.db);throw Error(g);};e.prototype.getRowsModified=function(){return F(this.db)};e.prototype.create_function=function(g,l){Object.prototype.hasOwnProperty.call(this.Na,g)&&(ua(this.Na[g]),delete this.Na[g]);var n=xa(function(t,w,z){w=b(w,z);try{var N=l.apply(null,
w)}catch(L){Aa(t,L,-1);return}a(t,N)},"viii");this.Na[g]=n;this.handleError(tb(this.db,g,l.length,1,0,n,0,0,0));return this};e.prototype.create_aggregate=function(g,l){var n=l.init||function(){return null},t=l.finalize||function(K){return K},w=l.step;if(!w)throw"An aggregate function must have a step function in "+g;var z={};Object.hasOwnProperty.call(this.Na,g)&&(ua(this.Na[g]),delete this.Na[g]);l=g+"__finalize";Object.hasOwnProperty.call(this.Na,l)&&(ua(this.Na[l]),delete this.Na[l]);var N=xa(function(K,
O,Ua){var X=ub(K,1);Object.hasOwnProperty.call(z,X)||(z[X]=n());O=b(O,Ua);O=[z[X]].concat(O);try{z[X]=w.apply(null,O)}catch(Nc){delete z[X],Aa(K,Nc,-1)}},"viii"),L=xa(function(K){var O=ub(K,1);try{var Ua=t(z[O])}catch(X){delete z[O];Aa(K,X,-1);return}a(K,Ua);delete z[O]},"vi");this.Na[g]=N;this.Na[l]=L;this.handleError(tb(this.db,g,w.length-1,1,0,0,N,L,0));return this};f.Database=e};
var ya=Object.assign({},f),za="./this.program",Ba="object"==typeof window,Ca="function"==typeof importScripts,Da="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,A="",Ea,Fa,Ga;
if(Da){var fs=require("fs"),Ha=require("path");A=Ca?Ha.dirname(A)+"/":__dirname+"/";Ea=(a,b)=>{a=Ia(a)?new URL(a):Ha.normalize(a);return fs.readFileSync(a,b?void 0:"utf8")};Ga=a=>{a=Ea(a,!0);a.buffer||(a=new Uint8Array(a));return a};Fa=(a,b,c,d=!0)=>{a=Ia(a)?new URL(a):Ha.normalize(a);fs.readFile(a,d?void 0:"utf8",(e,h)=>{e?c(e):b(d?h.buffer:h)})};!f.thisProgram&&1<process.argv.length&&(za=process.argv[1].replace(/\\/g,"/"));process.argv.slice(2);"undefined"!=typeof module&&(module.exports=f);f.inspect=
()=>"[Emscripten Module object]"}else if(Ba||Ca)Ca?A=self.location.href:"undefined"!=typeof document&&document.currentScript&&(A=document.currentScript.src),A=0!==A.indexOf("blob:")?A.substr(0,A.replace(/[?#].*/,"").lastIndexOf("/")+1):"",Ea=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.send(null);return b.responseText},Ca&&(Ga=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),Fa=(a,b,c)=>{var d=new XMLHttpRequest;d.open("GET",
a,!0);d.responseType="arraybuffer";d.onload=()=>{200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};var Ja=f.print||console.log.bind(console),B=f.printErr||console.error.bind(console);Object.assign(f,ya);ya=null;f.thisProgram&&(za=f.thisProgram);var Ka;f.wasmBinary&&(Ka=f.wasmBinary);"object"!=typeof WebAssembly&&C("no native wasm support detected");var La,Ma=!1,p,q,Na,D,E,Oa,Pa;
function Qa(){var a=La.buffer;f.HEAP8=p=new Int8Array(a);f.HEAP16=Na=new Int16Array(a);f.HEAPU8=q=new Uint8Array(a);f.HEAPU16=new Uint16Array(a);f.HEAP32=D=new Int32Array(a);f.HEAPU32=E=new Uint32Array(a);f.HEAPF32=Oa=new Float32Array(a);f.HEAPF64=Pa=new Float64Array(a)}var Ra=[],Sa=[],Ta=[];function Va(){var a=f.preRun.shift();Ra.unshift(a)}var G=0,Wa=null,Xa=null;
function C(a){f.onAbort?.(a);a="Aborted("+a+")";B(a);Ma=!0;throw new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");}var Ya=a=>a.startsWith("data:application/octet-stream;base64,"),Ia=a=>a.startsWith("file://"),Za;Za="sql-wasm.wasm";if(!Ya(Za)){var $a=Za;Za=f.locateFile?f.locateFile($a,A):A+$a}function ab(a){if(a==Za&&Ka)return new Uint8Array(Ka);if(Ga)return Ga(a);throw"both async and sync fetching of the wasm failed";}
function bb(a){if(!Ka&&(Ba||Ca)){if("function"==typeof fetch&&!Ia(a))return fetch(a,{credentials:"same-origin"}).then(b=>{if(!b.ok)throw"failed to load wasm binary file at '"+a+"'";return b.arrayBuffer()}).catch(()=>ab(a));if(Fa)return new Promise((b,c)=>{Fa(a,d=>b(new Uint8Array(d)),c)})}return Promise.resolve().then(()=>ab(a))}function cb(a,b,c){return bb(a).then(d=>WebAssembly.instantiate(d,b)).then(d=>d).then(c,d=>{B(`failed to asynchronously prepare wasm: ${d}`);C(d)})}
function db(a,b){var c=Za;Ka||"function"!=typeof WebAssembly.instantiateStreaming||Ya(c)||Ia(c)||Da||"function"!=typeof fetch?cb(c,a,b):fetch(c,{credentials:"same-origin"}).then(d=>WebAssembly.instantiateStreaming(d,a).then(b,function(e){B(`wasm streaming compile failed: ${e}`);B("falling back to ArrayBuffer instantiation");return cb(c,a,b)}))}var I,J,eb=a=>{for(;0<a.length;)a.shift()(f)};
function m(a,b="i8"){b.endsWith("*")&&(b="*");switch(b){case "i1":return p[a>>0];case "i8":return p[a>>0];case "i16":return Na[a>>1];case "i32":return D[a>>2];case "i64":C("to do getValue(i64) use WASM_BIGINT");case "float":return Oa[a>>2];case "double":return Pa[a>>3];case "*":return E[a>>2];default:C(`invalid type for getValue: ${b}`)}}
function qa(a){var b="i32";b.endsWith("*")&&(b="*");switch(b){case "i1":p[a>>0]=0;break;case "i8":p[a>>0]=0;break;case "i16":Na[a>>1]=0;break;case "i32":D[a>>2]=0;break;case "i64":C("to do setValue(i64) use WASM_BIGINT");case "float":Oa[a>>2]=0;break;case "double":Pa[a>>3]=0;break;case "*":E[a>>2]=0;break;default:C(`invalid type for setValue: ${b}`)}}
var fb="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0,M=(a,b,c)=>{var d=b+c;for(c=b;a[c]&&!(c>=d);)++c;if(16<c-b&&a.buffer&&fb)return fb.decode(a.subarray(b,c));for(d="";b<c;){var e=a[b++];if(e&128){var h=a[b++]&63;if(192==(e&224))d+=String.fromCharCode((e&31)<<6|h);else{var k=a[b++]&63;e=224==(e&240)?(e&15)<<12|h<<6|k:(e&7)<<18|h<<12|k<<6|a[b++]&63;65536>e?d+=String.fromCharCode(e):(e-=65536,d+=String.fromCharCode(55296|e>>10,56320|e&1023))}}else d+=String.fromCharCode(e)}return d},
ra=(a,b)=>a?M(q,a,b):"",gb=(a,b)=>{for(var c=0,d=a.length-1;0<=d;d--){var e=a[d];"."===e?a.splice(d,1):".."===e?(a.splice(d,1),c++):c&&(a.splice(d,1),c--)}if(b)for(;c;c--)a.unshift("..");return a},u=a=>{var b="/"===a.charAt(0),c="/"===a.substr(-1);(a=gb(a.split("/").filter(d=>!!d),!b).join("/"))||b||(a=".");a&&c&&(a+="/");return(b?"/":"")+a},hb=a=>{var b=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(a).slice(1);a=b[0];b=b[1];if(!a&&!b)return".";b&&=b.substr(0,b.length-1);return a+
b},ib=a=>{if("/"===a)return"/";a=u(a);a=a.replace(/\/$/,"");var b=a.lastIndexOf("/");return-1===b?a:a.substr(b+1)},jb=()=>{if("object"==typeof crypto&&"function"==typeof crypto.getRandomValues)return c=>crypto.getRandomValues(c);if(Da)try{var a=require("crypto");if(a.randomFillSync)return c=>a.randomFillSync(c);var b=a.randomBytes;return c=>(c.set(b(c.byteLength)),c)}catch(c){}C("initRandomDevice")},kb=a=>(kb=jb())(a);
function mb(){for(var a="",b=!1,c=arguments.length-1;-1<=c&&!b;c--){b=0<=c?arguments[c]:"/";if("string"!=typeof b)throw new TypeError("Arguments to path.resolve must be strings");if(!b)return"";a=b+"/"+a;b="/"===b.charAt(0)}a=gb(a.split("/").filter(d=>!!d),!b).join("/");return(b?"/":"")+a||"."}
var nb=[],da=a=>{for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=d?(b+=4,++c):b+=3}return b},fa=(a,b,c,d)=>{if(!(0<d))return 0;var e=c;d=c+d-1;for(var h=0;h<a.length;++h){var k=a.charCodeAt(h);if(55296<=k&&57343>=k){var r=a.charCodeAt(++h);k=65536+((k&1023)<<10)|r&1023}if(127>=k){if(c>=d)break;b[c++]=k}else{if(2047>=k){if(c+1>=d)break;b[c++]=192|k>>6}else{if(65535>=k){if(c+2>=d)break;b[c++]=224|k>>12}else{if(c+3>=d)break;b[c++]=240|k>>18;b[c++]=128|k>>
12&63}b[c++]=128|k>>6&63}b[c++]=128|k&63}}b[c]=0;return c-e};function oa(a,b){var c=Array(da(a)+1);a=fa(a,c,0,c.length);b&&(c.length=a);return c}var vb=[];function wb(a,b){vb[a]={input:[],output:[],Xa:b};xb(a,yb)}
var yb={open(a){var b=vb[a.node.rdev];if(!b)throw new P(43);a.tty=b;a.seekable=!1},close(a){a.tty.Xa.fsync(a.tty)},fsync(a){a.tty.Xa.fsync(a.tty)},read(a,b,c,d){if(!a.tty||!a.tty.Xa.sb)throw new P(60);for(var e=0,h=0;h<d;h++){try{var k=a.tty.Xa.sb(a.tty)}catch(r){throw new P(29);}if(void 0===k&&0===e)throw new P(6);if(null===k||void 0===k)break;e++;b[c+h]=k}e&&(a.node.timestamp=Date.now());return e},write(a,b,c,d){if(!a.tty||!a.tty.Xa.jb)throw new P(60);try{for(var e=0;e<d;e++)a.tty.Xa.jb(a.tty,b[c+
e])}catch(h){throw new P(29);}d&&(a.node.timestamp=Date.now());return e}},zb={sb(){a:{if(!nb.length){var a=null;if(Da){var b=Buffer.alloc(256),c=0,d=process.stdin.fd;try{c=fs.readSync(d,b)}catch(e){if(e.toString().includes("EOF"))c=0;else throw e;}0<c?a=b.slice(0,c).toString("utf-8"):a=null}else"undefined"!=typeof window&&"function"==typeof window.prompt?(a=window.prompt("Input: "),null!==a&&(a+="\n")):"function"==typeof readline&&(a=readline(),null!==a&&(a+="\n"));if(!a){a=null;break a}nb=oa(a,!0)}a=
nb.shift()}return a},jb(a,b){null===b||10===b?(Ja(M(a.output,0)),a.output=[]):0!=b&&a.output.push(b)},fsync(a){a.output&&0<a.output.length&&(Ja(M(a.output,0)),a.output=[])},Mb(){return{Ib:25856,Kb:5,Hb:191,Jb:35387,Gb:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},Nb(){return 0},Ob(){return[24,80]}},Ab={jb(a,b){null===b||10===b?(B(M(a.output,0)),a.output=[]):0!=b&&a.output.push(b)},fsync(a){a.output&&0<a.output.length&&(B(M(a.output,0)),a.output=[])}};
function Bb(a,b){var c=a.Ia?a.Ia.length:0;c>=b||(b=Math.max(b,c*(1048576>c?2:1.125)>>>0),0!=c&&(b=Math.max(b,256)),c=a.Ia,a.Ia=new Uint8Array(b),0<a.Ma&&a.Ia.set(c.subarray(0,a.Ma),0))}
var Q={Qa:null,Ra(){return Q.createNode(null,"/",16895,0)},createNode(a,b,c,d){if(24576===(c&61440)||4096===(c&61440))throw new P(63);Q.Qa||(Q.Qa={dir:{node:{Pa:Q.Ga.Pa,Oa:Q.Ga.Oa,lookup:Q.Ga.lookup,ab:Q.Ga.ab,rename:Q.Ga.rename,unlink:Q.Ga.unlink,rmdir:Q.Ga.rmdir,readdir:Q.Ga.readdir,symlink:Q.Ga.symlink},stream:{Ta:Q.Ha.Ta}},file:{node:{Pa:Q.Ga.Pa,Oa:Q.Ga.Oa},stream:{Ta:Q.Ha.Ta,read:Q.Ha.read,write:Q.Ha.write,lb:Q.Ha.lb,bb:Q.Ha.bb,cb:Q.Ha.cb}},link:{node:{Pa:Q.Ga.Pa,Oa:Q.Ga.Oa,readlink:Q.Ga.readlink},
stream:{}},pb:{node:{Pa:Q.Ga.Pa,Oa:Q.Ga.Oa},stream:Cb}});c=Db(a,b,c,d);R(c.mode)?(c.Ga=Q.Qa.dir.node,c.Ha=Q.Qa.dir.stream,c.Ia={}):32768===(c.mode&61440)?(c.Ga=Q.Qa.file.node,c.Ha=Q.Qa.file.stream,c.Ma=0,c.Ia=null):40960===(c.mode&61440)?(c.Ga=Q.Qa.link.node,c.Ha=Q.Qa.link.stream):8192===(c.mode&61440)&&(c.Ga=Q.Qa.pb.node,c.Ha=Q.Qa.pb.stream);c.timestamp=Date.now();a&&(a.Ia[b]=c,a.timestamp=c.timestamp);return c},Lb(a){return a.Ia?a.Ia.subarray?a.Ia.subarray(0,a.Ma):new Uint8Array(a.Ia):new Uint8Array(0)},
Ga:{Pa(a){var b={};b.dev=8192===(a.mode&61440)?a.id:1;b.ino=a.id;b.mode=a.mode;b.nlink=1;b.uid=0;b.gid=0;b.rdev=a.rdev;R(a.mode)?b.size=4096:32768===(a.mode&61440)?b.size=a.Ma:40960===(a.mode&61440)?b.size=a.link.length:b.size=0;b.atime=new Date(a.timestamp);b.mtime=new Date(a.timestamp);b.ctime=new Date(a.timestamp);b.zb=4096;b.blocks=Math.ceil(b.size/b.zb);return b},Oa(a,b){void 0!==b.mode&&(a.mode=b.mode);void 0!==b.timestamp&&(a.timestamp=b.timestamp);if(void 0!==b.size&&(b=b.size,a.Ma!=b))if(0==
b)a.Ia=null,a.Ma=0;else{var c=a.Ia;a.Ia=new Uint8Array(b);c&&a.Ia.set(c.subarray(0,Math.min(b,a.Ma)));a.Ma=b}},lookup(){throw Eb[44];},ab(a,b,c,d){return Q.createNode(a,b,c,d)},rename(a,b,c){if(R(a.mode)){try{var d=Fb(b,c)}catch(h){}if(d)for(var e in d.Ia)throw new P(55);}delete a.parent.Ia[a.name];a.parent.timestamp=Date.now();a.name=c;b.Ia[c]=a;b.timestamp=a.parent.timestamp;a.parent=b},unlink(a,b){delete a.Ia[b];a.timestamp=Date.now()},rmdir(a,b){var c=Fb(a,b),d;for(d in c.Ia)throw new P(55);delete a.Ia[b];
a.timestamp=Date.now()},readdir(a){var b=[".",".."],c;for(c of Object.keys(a.Ia))b.push(c);return b},symlink(a,b,c){a=Q.createNode(a,b,41471,0);a.link=c;return a},readlink(a){if(40960!==(a.mode&61440))throw new P(28);return a.link}},Ha:{read(a,b,c,d,e){var h=a.node.Ia;if(e>=a.node.Ma)return 0;a=Math.min(a.node.Ma-e,d);if(8<a&&h.subarray)b.set(h.subarray(e,e+a),c);else for(d=0;d<a;d++)b[c+d]=h[e+d];return a},write(a,b,c,d,e,h){b.buffer===p.buffer&&(h=!1);if(!d)return 0;a=a.node;a.timestamp=Date.now();
if(b.subarray&&(!a.Ia||a.Ia.subarray)){if(h)return a.Ia=b.subarray(c,c+d),a.Ma=d;if(0===a.Ma&&0===e)return a.Ia=b.slice(c,c+d),a.Ma=d;if(e+d<=a.Ma)return a.Ia.set(b.subarray(c,c+d),e),d}Bb(a,e+d);if(a.Ia.subarray&&b.subarray)a.Ia.set(b.subarray(c,c+d),e);else for(h=0;h<d;h++)a.Ia[e+h]=b[c+h];a.Ma=Math.max(a.Ma,e+d);return d},Ta(a,b,c){1===c?b+=a.position:2===c&&32768===(a.node.mode&61440)&&(b+=a.node.Ma);if(0>b)throw new P(28);return b},lb(a,b,c){Bb(a.node,b+c);a.node.Ma=Math.max(a.node.Ma,b+c)},
bb(a,b,c,d,e){if(32768!==(a.node.mode&61440))throw new P(43);a=a.node.Ia;if(e&2||a.buffer!==p.buffer){if(0<c||c+b<a.length)a.subarray?a=a.subarray(c,c+b):a=Array.prototype.slice.call(a,c,c+b);c=!0;b=65536*Math.ceil(b/65536);(e=Gb(65536,b))?(q.fill(0,e,e+b),b=e):b=0;if(!b)throw new P(48);p.set(a,b)}else c=!1,b=a.byteOffset;return{Db:b,ub:c}},cb(a,b,c,d){Q.Ha.write(a,b,0,d,c,!1);return 0}}},ia=(a,b)=>{var c=0;a&&(c|=365);b&&(c|=146);return c},Hb=null,Ib={},Jb=[],Kb=1,S=null,Lb=!0,P=null,Eb={};
function T(a,b={}){a=mb(a);if(!a)return{path:"",node:null};b=Object.assign({qb:!0,kb:0},b);if(8<b.kb)throw new P(32);a=a.split("/").filter(k=>!!k);for(var c=Hb,d="/",e=0;e<a.length;e++){var h=e===a.length-1;if(h&&b.parent)break;c=Fb(c,a[e]);d=u(d+"/"+a[e]);c.Va&&(!h||h&&b.qb)&&(c=c.Va.root);if(!h||b.Sa)for(h=0;40960===(c.mode&61440);)if(c=Mb(d),d=mb(hb(d),c),c=T(d,{kb:b.kb+1}).node,40<h++)throw new P(32);}return{path:d,node:c}}
function ha(a){for(var b;;){if(a===a.parent)return a=a.Ra.tb,b?"/"!==a[a.length-1]?`${a}/${b}`:a+b:a;b=b?`${a.name}/${b}`:a.name;a=a.parent}}function Nb(a,b){for(var c=0,d=0;d<b.length;d++)c=(c<<5)-c+b.charCodeAt(d)|0;return(a+c>>>0)%S.length}function Ob(a){var b=Nb(a.parent.id,a.name);if(S[b]===a)S[b]=a.Wa;else for(b=S[b];b;){if(b.Wa===a){b.Wa=a.Wa;break}b=b.Wa}}
function Fb(a,b){var c;if(c=(c=Pb(a,"x"))?c:a.Ga.lookup?0:2)throw new P(c,a);for(c=S[Nb(a.id,b)];c;c=c.Wa){var d=c.name;if(c.parent.id===a.id&&d===b)return c}return a.Ga.lookup(a,b)}function Db(a,b,c,d){a=new Qb(a,b,c,d);b=Nb(a.parent.id,a.name);a.Wa=S[b];return S[b]=a}function R(a){return 16384===(a&61440)}function Rb(a){var b=["r","w","rw"][a&3];a&512&&(b+="w");return b}
function Pb(a,b){if(Lb)return 0;if(!b.includes("r")||a.mode&292){if(b.includes("w")&&!(a.mode&146)||b.includes("x")&&!(a.mode&73))return 2}else return 2;return 0}function Sb(a,b){try{return Fb(a,b),20}catch(c){}return Pb(a,"wx")}function Tb(a,b,c){try{var d=Fb(a,b)}catch(e){return e.Ka}if(a=Pb(a,"wx"))return a;if(c){if(!R(d.mode))return 54;if(d===d.parent||"/"===ha(d))return 10}else if(R(d.mode))return 31;return 0}function Ub(){for(var a=0;4096>=a;a++)if(!Jb[a])return a;throw new P(33);}
function U(a){a=Jb[a];if(!a)throw new P(8);return a}function Vb(a,b=-1){Wb||(Wb=function(){this.$a={}},Wb.prototype={},Object.defineProperties(Wb.prototype,{object:{get(){return this.node},set(c){this.node=c}},flags:{get(){return this.$a.flags},set(c){this.$a.flags=c}},position:{get(){return this.$a.position},set(c){this.$a.position=c}}}));a=Object.assign(new Wb,a);-1==b&&(b=Ub());a.fd=b;return Jb[b]=a}var Cb={open(a){a.Ha=Ib[a.node.rdev].Ha;a.Ha.open?.(a)},Ta(){throw new P(70);}};
function xb(a,b){Ib[a]={Ha:b}}function Xb(a,b){var c="/"===b,d=!b;if(c&&Hb)throw new P(10);if(!c&&!d){var e=T(b,{qb:!1});b=e.path;e=e.node;if(e.Va)throw new P(10);if(!R(e.mode))throw new P(54);}b={type:a,Pb:{},tb:b,Cb:[]};a=a.Ra(b);a.Ra=b;b.root=a;c?Hb=a:e&&(e.Va=b,e.Ra&&e.Ra.Cb.push(b))}function ja(a,b,c){var d=T(a,{parent:!0}).node;a=ib(a);if(!a||"."===a||".."===a)throw new P(28);var e=Sb(d,a);if(e)throw new P(e);if(!d.Ga.ab)throw new P(63);return d.Ga.ab(d,a,b,c)}
function V(a,b){return ja(a,(void 0!==b?b:511)&1023|16384,0)}function Yb(a,b,c){"undefined"==typeof c&&(c=b,b=438);ja(a,b|8192,c)}function Zb(a,b){if(!mb(a))throw new P(44);var c=T(b,{parent:!0}).node;if(!c)throw new P(44);b=ib(b);var d=Sb(c,b);if(d)throw new P(d);if(!c.Ga.symlink)throw new P(63);c.Ga.symlink(c,b,a)}function $b(a){var b=T(a,{parent:!0}).node;a=ib(a);var c=Fb(b,a),d=Tb(b,a,!0);if(d)throw new P(d);if(!b.Ga.rmdir)throw new P(63);if(c.Va)throw new P(10);b.Ga.rmdir(b,a);Ob(c)}
function wa(a){var b=T(a,{parent:!0}).node;if(!b)throw new P(44);a=ib(a);var c=Fb(b,a),d=Tb(b,a,!1);if(d)throw new P(d);if(!b.Ga.unlink)throw new P(63);if(c.Va)throw new P(10);b.Ga.unlink(b,a);Ob(c)}function Mb(a){a=T(a).node;if(!a)throw new P(44);if(!a.Ga.readlink)throw new P(28);return mb(ha(a.parent),a.Ga.readlink(a))}function ac(a,b){a=T(a,{Sa:!b}).node;if(!a)throw new P(44);if(!a.Ga.Pa)throw new P(63);return a.Ga.Pa(a)}function bc(a){return ac(a,!0)}
function ka(a,b){a="string"==typeof a?T(a,{Sa:!0}).node:a;if(!a.Ga.Oa)throw new P(63);a.Ga.Oa(a,{mode:b&4095|a.mode&-4096,timestamp:Date.now()})}function cc(a,b){if(0>b)throw new P(28);a="string"==typeof a?T(a,{Sa:!0}).node:a;if(!a.Ga.Oa)throw new P(63);if(R(a.mode))throw new P(31);if(32768!==(a.mode&61440))throw new P(28);var c=Pb(a,"w");if(c)throw new P(c);a.Ga.Oa(a,{size:b,timestamp:Date.now()})}
function la(a,b,c){if(""===a)throw new P(44);if("string"==typeof b){var d={r:0,"r+":2,w:577,"w+":578,a:1089,"a+":1090}[b];if("undefined"==typeof d)throw Error(`Unknown file open mode: ${b}`);b=d}c=b&64?("undefined"==typeof c?438:c)&4095|32768:0;if("object"==typeof a)var e=a;else{a=u(a);try{e=T(a,{Sa:!(b&131072)}).node}catch(h){}}d=!1;if(b&64)if(e){if(b&128)throw new P(20);}else e=ja(a,c,0),d=!0;if(!e)throw new P(44);8192===(e.mode&61440)&&(b&=-513);if(b&65536&&!R(e.mode))throw new P(54);if(!d&&(c=
e?40960===(e.mode&61440)?32:R(e.mode)&&("r"!==Rb(b)||b&512)?31:Pb(e,Rb(b)):44))throw new P(c);b&512&&!d&&cc(e,0);b&=-131713;e=Vb({node:e,path:ha(e),flags:b,seekable:!0,position:0,Ha:e.Ha,Fb:[],error:!1});e.Ha.open&&e.Ha.open(e);!f.logReadFiles||b&1||(dc||={},a in dc||(dc[a]=1));return e}function na(a){if(null===a.fd)throw new P(8);a.hb&&(a.hb=null);try{a.Ha.close&&a.Ha.close(a)}catch(b){throw b;}finally{Jb[a.fd]=null}a.fd=null}
function ec(a,b,c){if(null===a.fd)throw new P(8);if(!a.seekable||!a.Ha.Ta)throw new P(70);if(0!=c&&1!=c&&2!=c)throw new P(28);a.position=a.Ha.Ta(a,b,c);a.Fb=[]}function fc(a,b,c,d,e){if(0>d||0>e)throw new P(28);if(null===a.fd)throw new P(8);if(1===(a.flags&2097155))throw new P(8);if(R(a.node.mode))throw new P(31);if(!a.Ha.read)throw new P(28);var h="undefined"!=typeof e;if(!h)e=a.position;else if(!a.seekable)throw new P(70);b=a.Ha.read(a,b,c,d,e);h||(a.position+=b);return b}
function ma(a,b,c,d,e){if(0>d||0>e)throw new P(28);if(null===a.fd)throw new P(8);if(0===(a.flags&2097155))throw new P(8);if(R(a.node.mode))throw new P(31);if(!a.Ha.write)throw new P(28);a.seekable&&a.flags&1024&&ec(a,0,2);var h="undefined"!=typeof e;if(!h)e=a.position;else if(!a.seekable)throw new P(70);b=a.Ha.write(a,b,c,d,e,void 0);h||(a.position+=b);return b}
function va(a){var b="binary";if("utf8"!==b&&"binary"!==b)throw Error(`Invalid encoding type "${b}"`);var c;var d=la(a,d||0);a=ac(a).size;var e=new Uint8Array(a);fc(d,e,0,a,0);"utf8"===b?c=M(e,0):"binary"===b&&(c=e);na(d);return c}function gc(){P||(P=function(a,b){this.name="ErrnoError";this.node=b;this.Eb=function(c){this.Ka=c};this.Eb(a);this.message="FS error"},P.prototype=Error(),P.prototype.constructor=P,[44].forEach(a=>{Eb[a]=new P(a);Eb[a].stack="<generic error, no stack>"}))}var hc;
function ic(a,b,c){a=u("/dev/"+a);var d=ia(!!b,!!c);jc||=64;var e=jc++<<8|0;xb(e,{open(h){h.seekable=!1},close(){c?.buffer?.length&&c(10)},read(h,k,r,y){for(var v=0,F=0;F<y;F++){try{var H=b()}catch(pb){throw new P(29);}if(void 0===H&&0===v)throw new P(6);if(null===H||void 0===H)break;v++;k[r+F]=H}v&&(h.node.timestamp=Date.now());return v},write(h,k,r,y){for(var v=0;v<y;v++)try{c(k[r+v])}catch(F){throw new P(29);}y&&(h.node.timestamp=Date.now());return v}});Yb(a,d,e)}var jc,W={},Wb,dc;
function kc(a,b,c){if("/"===b.charAt(0))return b;a=-100===a?"/":U(a).path;if(0==b.length){if(!c)throw new P(44);return a}return u(a+"/"+b)}
function lc(a,b,c){try{var d=a(b)}catch(h){if(h&&h.node&&u(b)!==u(ha(h.node)))return-54;throw h;}D[c>>2]=d.dev;D[c+4>>2]=d.mode;E[c+8>>2]=d.nlink;D[c+12>>2]=d.uid;D[c+16>>2]=d.gid;D[c+20>>2]=d.rdev;J=[d.size>>>0,(I=d.size,1<=+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[c+24>>2]=J[0];D[c+28>>2]=J[1];D[c+32>>2]=4096;D[c+36>>2]=d.blocks;a=d.atime.getTime();b=d.mtime.getTime();var e=d.ctime.getTime();J=[Math.floor(a/1E3)>>>0,(I=Math.floor(a/1E3),1<=
+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[c+40>>2]=J[0];D[c+44>>2]=J[1];E[c+48>>2]=a%1E3*1E3;J=[Math.floor(b/1E3)>>>0,(I=Math.floor(b/1E3),1<=+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[c+56>>2]=J[0];D[c+60>>2]=J[1];E[c+64>>2]=b%1E3*1E3;J=[Math.floor(e/1E3)>>>0,(I=Math.floor(e/1E3),1<=+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[c+72>>2]=J[0];
D[c+76>>2]=J[1];E[c+80>>2]=e%1E3*1E3;J=[d.ino>>>0,(I=d.ino,1<=+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[c+88>>2]=J[0];D[c+92>>2]=J[1];return 0}var Mc=void 0;function Oc(){var a=D[+Mc>>2];Mc+=4;return a}
var Pc=(a,b)=>b+2097152>>>0<4194305-!!a?(a>>>0)+4294967296*b:NaN,Qc=[0,31,60,91,121,152,182,213,244,274,305,335],Rc=[0,31,59,90,120,151,181,212,243,273,304,334],Sc=a=>{var b=da(a)+1,c=ea(b);c&&fa(a,q,c,b);return c},Tc={},Vc=()=>{if(!Uc){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:za||"./this.program"},b;for(b in Tc)void 0===Tc[b]?delete a[b]:a[b]=Tc[b];
var c=[];for(b in a)c.push(`${b}=${a[b]}`);Uc=c}return Uc},Uc,ta=a=>{var b=da(a)+1,c=x(b);fa(a,q,c,b);return c},Wc=(a,b,c,d)=>{var e={string:v=>{var F=0;null!==v&&void 0!==v&&0!==v&&(F=ta(v));return F},array:v=>{var F=x(v.length);p.set(v,F);return F}};a=f["_"+a];var h=[],k=0;if(d)for(var r=0;r<d.length;r++){var y=e[c[r]];y?(0===k&&(k=pa()),h[r]=y(d[r])):h[r]=d[r]}c=a.apply(null,h);return c=function(v){0!==k&&sa(k);return"string"===b?v?M(q,v):"":"boolean"===b?!!v:v}(c)},ba=0,aa=(a,b)=>{b=1==b?x(a.length):
ea(a.length);a.subarray||a.slice||(a=new Uint8Array(a));q.set(a,b);return b},Xc,Yc=[],Y,ua=a=>{Xc.delete(Y.get(a));Y.set(a,null);Yc.push(a)},xa=(a,b)=>{if(!Xc){Xc=new WeakMap;var c=Y.length;if(Xc)for(var d=0;d<0+c;d++){var e=Y.get(d);e&&Xc.set(e,d)}}if(c=Xc.get(a)||0)return c;if(Yc.length)c=Yc.pop();else{try{Y.grow(1)}catch(r){if(!(r instanceof RangeError))throw r;throw"Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.";}c=Y.length-1}try{Y.set(c,a)}catch(r){if(!(r instanceof TypeError))throw r;if("function"==
typeof WebAssembly.Function){d=WebAssembly.Function;e={i:"i32",j:"i64",f:"f32",d:"f64",e:"externref",p:"i32"};for(var h={parameters:[],results:"v"==b[0]?[]:[e[b[0]]]},k=1;k<b.length;++k)h.parameters.push(e[b[k]]);b=new d(h,a)}else{d=[1];e=b.slice(0,1);b=b.slice(1);h={i:127,p:127,j:126,f:125,d:124,e:111};d.push(96);k=b.length;128>k?d.push(k):d.push(k%128|128,k>>7);for(k=0;k<b.length;++k)d.push(h[b[k]]);"v"==e?d.push(0):d.push(1,h[e]);b=[0,97,115,109,1,0,0,0,1];e=d.length;128>e?b.push(e):b.push(e%128|
128,e>>7);b.push.apply(b,d);b.push(2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0);b=new WebAssembly.Module(new Uint8Array(b));b=(new WebAssembly.Instance(b,{e:{f:a}})).exports.f}Y.set(c,b)}Xc.set(a,c);return c};function Qb(a,b,c,d){a||=this;this.parent=a;this.Ra=a.Ra;this.Va=null;this.id=Kb++;this.name=b;this.mode=c;this.Ga={};this.Ha={};this.rdev=d}
Object.defineProperties(Qb.prototype,{read:{get:function(){return 365===(this.mode&365)},set:function(a){a?this.mode|=365:this.mode&=-366}},write:{get:function(){return 146===(this.mode&146)},set:function(a){a?this.mode|=146:this.mode&=-147}}});gc();S=Array(4096);Xb(Q,"/");V("/tmp");V("/home");V("/home/web_user");
(function(){V("/dev");xb(259,{read:()=>0,write:(d,e,h,k)=>k});Yb("/dev/null",259);wb(1280,zb);wb(1536,Ab);Yb("/dev/tty",1280);Yb("/dev/tty1",1536);var a=new Uint8Array(1024),b=0,c=()=>{0===b&&(b=kb(a).byteLength);return a[--b]};ic("random",c);ic("urandom",c);V("/dev/shm");V("/dev/shm/tmp")})();
(function(){V("/proc");var a=V("/proc/self");V("/proc/self/fd");Xb({Ra(){var b=Db(a,"fd",16895,73);b.Ga={lookup(c,d){var e=U(+d);c={parent:null,Ra:{tb:"fake"},Ga:{readlink:()=>e.path}};return c.parent=c}};return b}},"/proc/self/fd")})();
var $c={a:(a,b,c,d)=>{C(`Assertion failed: ${a?M(q,a):""}, at: `+[b?b?M(q,b):"":"unknown filename",c,d?d?M(q,d):"":"unknown function"])},h:function(a,b){try{return a=a?M(q,a):"",ka(a,b),0}catch(c){if("undefined"==typeof W||"ErrnoError"!==c.name)throw c;return-c.Ka}},H:function(a,b,c){try{b=b?M(q,b):"";b=kc(a,b);if(c&-8)return-28;var d=T(b,{Sa:!0}).node;if(!d)return-44;a="";c&4&&(a+="r");c&2&&(a+="w");c&1&&(a+="x");return a&&Pb(d,a)?-2:0}catch(e){if("undefined"==typeof W||"ErrnoError"!==e.name)throw e;
return-e.Ka}},i:function(a,b){try{var c=U(a);ka(c.node,b);return 0}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return-d.Ka}},g:function(a){try{var b=U(a).node;var c="string"==typeof b?T(b,{Sa:!0}).node:b;if(!c.Ga.Oa)throw new P(63);c.Ga.Oa(c,{timestamp:Date.now()});return 0}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return-d.Ka}},b:function(a,b,c){Mc=c;try{var d=U(a);switch(b){case 0:var e=Oc();if(0>e)return-28;for(;Jb[e];)e++;return Vb(d,e).fd;case 1:case 2:return 0;
case 3:return d.flags;case 4:return e=Oc(),d.flags|=e,0;case 5:return e=Oc(),Na[e+0>>1]=2,0;case 6:case 7:return 0;case 16:case 8:return-28;case 9:return D[Zc()>>2]=28,-1;default:return-28}}catch(h){if("undefined"==typeof W||"ErrnoError"!==h.name)throw h;return-h.Ka}},f:function(a,b){try{var c=U(a);return lc(ac,c.path,b)}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return-d.Ka}},n:function(a,b,c){b=Pc(b,c);try{if(isNaN(b))return 61;var d=U(a);if(0===(d.flags&2097155))throw new P(28);
cc(d.node,b);return 0}catch(e){if("undefined"==typeof W||"ErrnoError"!==e.name)throw e;return-e.Ka}},C:function(a,b){try{if(0===b)return-28;var c=da("/")+1;if(b<c)return-68;fa("/",q,a,b);return c}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return-d.Ka}},F:function(a,b){try{return a=a?M(q,a):"",lc(bc,a,b)}catch(c){if("undefined"==typeof W||"ErrnoError"!==c.name)throw c;return-c.Ka}},z:function(a,b,c){try{return b=b?M(q,b):"",b=kc(a,b),b=u(b),"/"===b[b.length-1]&&(b=b.substr(0,
b.length-1)),V(b,c),0}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return-d.Ka}},E:function(a,b,c,d){try{b=b?M(q,b):"";var e=d&256;b=kc(a,b,d&4096);return lc(e?bc:ac,b,c)}catch(h){if("undefined"==typeof W||"ErrnoError"!==h.name)throw h;return-h.Ka}},y:function(a,b,c,d){Mc=d;try{b=b?M(q,b):"";b=kc(a,b);var e=d?Oc():0;return la(b,c,e).fd}catch(h){if("undefined"==typeof W||"ErrnoError"!==h.name)throw h;return-h.Ka}},w:function(a,b,c,d){try{b=b?M(q,b):"";b=kc(a,b);if(0>=d)return-28;
var e=Mb(b),h=Math.min(d,da(e)),k=p[c+h];fa(e,q,c,d+1);p[c+h]=k;return h}catch(r){if("undefined"==typeof W||"ErrnoError"!==r.name)throw r;return-r.Ka}},v:function(a){try{return a=a?M(q,a):"",$b(a),0}catch(b){if("undefined"==typeof W||"ErrnoError"!==b.name)throw b;return-b.Ka}},G:function(a,b){try{return a=a?M(q,a):"",lc(ac,a,b)}catch(c){if("undefined"==typeof W||"ErrnoError"!==c.name)throw c;return-c.Ka}},r:function(a,b,c){try{return b=b?M(q,b):"",b=kc(a,b),0===c?wa(b):512===c?$b(b):C("Invalid flags passed to unlinkat"),
0}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return-d.Ka}},q:function(a,b,c){try{b=b?M(q,b):"";b=kc(a,b,!0);if(c){var d=E[c>>2]+4294967296*D[c+4>>2],e=D[c+8>>2];h=1E3*d+e/1E6;c+=16;d=E[c>>2]+4294967296*D[c+4>>2];e=D[c+8>>2];k=1E3*d+e/1E6}else var h=Date.now(),k=h;a=h;var r=T(b,{Sa:!0}).node;r.Ga.Oa(r,{timestamp:Math.max(a,k)});return 0}catch(y){if("undefined"==typeof W||"ErrnoError"!==y.name)throw y;return-y.Ka}},l:function(a,b,c){a=new Date(1E3*Pc(a,b));D[c>>2]=a.getSeconds();
D[c+4>>2]=a.getMinutes();D[c+8>>2]=a.getHours();D[c+12>>2]=a.getDate();D[c+16>>2]=a.getMonth();D[c+20>>2]=a.getFullYear()-1900;D[c+24>>2]=a.getDay();b=a.getFullYear();D[c+28>>2]=(0!==b%4||0===b%100&&0!==b%400?Rc:Qc)[a.getMonth()]+a.getDate()-1|0;D[c+36>>2]=-(60*a.getTimezoneOffset());b=(new Date(a.getFullYear(),6,1)).getTimezoneOffset();var d=(new Date(a.getFullYear(),0,1)).getTimezoneOffset();D[c+32>>2]=(b!=d&&a.getTimezoneOffset()==Math.min(d,b))|0},j:function(a,b,c,d,e,h,k,r){e=Pc(e,h);try{if(isNaN(e))return 61;
var y=U(d);if(0!==(b&2)&&0===(c&2)&&2!==(y.flags&2097155))throw new P(2);if(1===(y.flags&2097155))throw new P(2);if(!y.Ha.bb)throw new P(43);var v=y.Ha.bb(y,a,e,b,c);var F=v.Db;D[k>>2]=v.ub;E[r>>2]=F;return 0}catch(H){if("undefined"==typeof W||"ErrnoError"!==H.name)throw H;return-H.Ka}},k:function(a,b,c,d,e,h,k){h=Pc(h,k);try{if(isNaN(h))return 61;var r=U(e);if(c&2){if(32768!==(r.node.mode&61440))throw new P(43);if(!(d&2)){var y=q.slice(a,a+b);r.Ha.cb&&r.Ha.cb(r,y,h,b,d)}}}catch(v){if("undefined"==
typeof W||"ErrnoError"!==v.name)throw v;return-v.Ka}},s:(a,b,c)=>{function d(y){return(y=y.toTimeString().match(/\(([A-Za-z ]+)\)$/))?y[1]:"GMT"}var e=(new Date).getFullYear(),h=new Date(e,0,1),k=new Date(e,6,1);e=h.getTimezoneOffset();var r=k.getTimezoneOffset();E[a>>2]=60*Math.max(e,r);D[b>>2]=Number(e!=r);a=d(h);b=d(k);a=Sc(a);b=Sc(b);r<e?(E[c>>2]=a,E[c+4>>2]=b):(E[c>>2]=b,E[c+4>>2]=a)},d:()=>Date.now(),t:()=>2147483648,c:()=>performance.now(),o:a=>{var b=q.length;a>>>=0;if(2147483648<a)return!1;
for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);var e=Math;d=Math.max(a,d);a:{e=(e.min.call(e,2147483648,d+(65536-d%65536)%65536)-La.buffer.byteLength+65535)/65536;try{La.grow(e);Qa();var h=1;break a}catch(k){}h=void 0}if(h)return!0}return!1},A:(a,b)=>{var c=0;Vc().forEach((d,e)=>{var h=b+c;e=E[a+4*e>>2]=h;for(h=0;h<d.length;++h)p[e++>>0]=d.charCodeAt(h);p[e>>0]=0;c+=d.length+1});return 0},B:(a,b)=>{var c=Vc();E[a>>2]=c.length;var d=0;c.forEach(e=>d+=e.length+1);E[b>>2]=d;return 0},
e:function(a){try{var b=U(a);na(b);return 0}catch(c){if("undefined"==typeof W||"ErrnoError"!==c.name)throw c;return c.Ka}},p:function(a,b){try{var c=U(a);p[b>>0]=c.tty?2:R(c.mode)?3:40960===(c.mode&61440)?7:4;Na[b+2>>1]=0;J=[0,(I=0,1<=+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[b+8>>2]=J[0];D[b+12>>2]=J[1];J=[0,(I=0,1<=+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[b+16>>2]=J[0];D[b+20>>2]=J[1];
return 0}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return d.Ka}},x:function(a,b,c,d){try{a:{var e=U(a);a=b;for(var h,k=b=0;k<c;k++){var r=E[a>>2],y=E[a+4>>2];a+=8;var v=fc(e,p,r,y,h);if(0>v){var F=-1;break a}b+=v;if(v<y)break;"undefined"!==typeof h&&(h+=v)}F=b}E[d>>2]=F;return 0}catch(H){if("undefined"==typeof W||"ErrnoError"!==H.name)throw H;return H.Ka}},m:function(a,b,c,d,e){b=Pc(b,c);try{if(isNaN(b))return 61;var h=U(a);ec(h,b,d);J=[h.position>>>0,(I=h.position,1<=+Math.abs(I)?
0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[e>>2]=J[0];D[e+4>>2]=J[1];h.hb&&0===b&&0===d&&(h.hb=null);return 0}catch(k){if("undefined"==typeof W||"ErrnoError"!==k.name)throw k;return k.Ka}},D:function(a){try{var b=U(a);return b.Ha?.fsync?b.Ha.fsync(b):0}catch(c){if("undefined"==typeof W||"ErrnoError"!==c.name)throw c;return c.Ka}},u:function(a,b,c,d){try{a:{var e=U(a);a=b;for(var h,k=b=0;k<c;k++){var r=E[a>>2],y=E[a+4>>2];a+=8;var v=ma(e,p,r,y,h);if(0>v){var F=
-1;break a}b+=v;"undefined"!==typeof h&&(h+=v)}F=b}E[d>>2]=F;return 0}catch(H){if("undefined"==typeof W||"ErrnoError"!==H.name)throw H;return H.Ka}}},Z=function(){function a(c){Z=c.exports;La=Z.I;Qa();Y=Z.Aa;Sa.unshift(Z.J);G--;f.monitorRunDependencies?.(G);0==G&&(null!==Wa&&(clearInterval(Wa),Wa=null),Xa&&(c=Xa,Xa=null,c()));return Z}var b={a:$c};G++;f.monitorRunDependencies?.(G);if(f.instantiateWasm)try{return f.instantiateWasm(b,a)}catch(c){return B(`Module.instantiateWasm callback failed with error: ${c}`),
!1}db(b,function(c){a(c.instance)});return{}}();f._sqlite3_free=a=>(f._sqlite3_free=Z.K)(a);f._sqlite3_value_text=a=>(f._sqlite3_value_text=Z.L)(a);var Zc=()=>(Zc=Z.M)();f._sqlite3_prepare_v2=(a,b,c,d,e)=>(f._sqlite3_prepare_v2=Z.N)(a,b,c,d,e);f._sqlite3_step=a=>(f._sqlite3_step=Z.O)(a);f._sqlite3_reset=a=>(f._sqlite3_reset=Z.P)(a);f._sqlite3_exec=(a,b,c,d,e)=>(f._sqlite3_exec=Z.Q)(a,b,c,d,e);f._sqlite3_finalize=a=>(f._sqlite3_finalize=Z.R)(a);
f._sqlite3_column_name=(a,b)=>(f._sqlite3_column_name=Z.S)(a,b);f._sqlite3_column_text=(a,b)=>(f._sqlite3_column_text=Z.T)(a,b);f._sqlite3_column_type=(a,b)=>(f._sqlite3_column_type=Z.U)(a,b);f._sqlite3_errmsg=a=>(f._sqlite3_errmsg=Z.V)(a);f._sqlite3_clear_bindings=a=>(f._sqlite3_clear_bindings=Z.W)(a);f._sqlite3_value_blob=a=>(f._sqlite3_value_blob=Z.X)(a);f._sqlite3_value_bytes=a=>(f._sqlite3_value_bytes=Z.Y)(a);f._sqlite3_value_double=a=>(f._sqlite3_value_double=Z.Z)(a);
f._sqlite3_value_int=a=>(f._sqlite3_value_int=Z._)(a);f._sqlite3_value_type=a=>(f._sqlite3_value_type=Z.$)(a);f._sqlite3_result_blob=(a,b,c,d)=>(f._sqlite3_result_blob=Z.aa)(a,b,c,d);f._sqlite3_result_double=(a,b)=>(f._sqlite3_result_double=Z.ba)(a,b);f._sqlite3_result_error=(a,b,c)=>(f._sqlite3_result_error=Z.ca)(a,b,c);f._sqlite3_result_int=(a,b)=>(f._sqlite3_result_int=Z.da)(a,b);f._sqlite3_result_int64=(a,b,c)=>(f._sqlite3_result_int64=Z.ea)(a,b,c);
f._sqlite3_result_null=a=>(f._sqlite3_result_null=Z.fa)(a);f._sqlite3_result_text=(a,b,c,d)=>(f._sqlite3_result_text=Z.ga)(a,b,c,d);f._sqlite3_aggregate_context=(a,b)=>(f._sqlite3_aggregate_context=Z.ha)(a,b);f._sqlite3_column_count=a=>(f._sqlite3_column_count=Z.ia)(a);f._sqlite3_data_count=a=>(f._sqlite3_data_count=Z.ja)(a);f._sqlite3_column_blob=(a,b)=>(f._sqlite3_column_blob=Z.ka)(a,b);f._sqlite3_column_bytes=(a,b)=>(f._sqlite3_column_bytes=Z.la)(a,b);
f._sqlite3_column_double=(a,b)=>(f._sqlite3_column_double=Z.ma)(a,b);f._sqlite3_bind_blob=(a,b,c,d,e)=>(f._sqlite3_bind_blob=Z.na)(a,b,c,d,e);f._sqlite3_bind_double=(a,b,c)=>(f._sqlite3_bind_double=Z.oa)(a,b,c);f._sqlite3_bind_int=(a,b,c)=>(f._sqlite3_bind_int=Z.pa)(a,b,c);f._sqlite3_bind_text=(a,b,c,d,e)=>(f._sqlite3_bind_text=Z.qa)(a,b,c,d,e);f._sqlite3_bind_parameter_index=(a,b)=>(f._sqlite3_bind_parameter_index=Z.ra)(a,b);f._sqlite3_sql=a=>(f._sqlite3_sql=Z.sa)(a);
f._sqlite3_normalized_sql=a=>(f._sqlite3_normalized_sql=Z.ta)(a);f._sqlite3_changes=a=>(f._sqlite3_changes=Z.ua)(a);f._sqlite3_close_v2=a=>(f._sqlite3_close_v2=Z.va)(a);f._sqlite3_create_function_v2=(a,b,c,d,e,h,k,r,y)=>(f._sqlite3_create_function_v2=Z.wa)(a,b,c,d,e,h,k,r,y);f._sqlite3_open=(a,b)=>(f._sqlite3_open=Z.xa)(a,b);var ea=f._malloc=a=>(ea=f._malloc=Z.ya)(a),ca=f._free=a=>(ca=f._free=Z.za)(a);f._RegisterExtensionFunctions=a=>(f._RegisterExtensionFunctions=Z.Ba)(a);
var Gb=(a,b)=>(Gb=Z.Ca)(a,b),pa=()=>(pa=Z.Da)(),sa=a=>(sa=Z.Ea)(a),x=a=>(x=Z.Fa)(a);f.stackAlloc=x;f.stackSave=pa;f.stackRestore=sa;f.cwrap=(a,b,c,d)=>{var e=!c||c.every(h=>"number"===h||"boolean"===h);return"string"!==b&&e&&!d?f["_"+a]:function(){return Wc(a,b,c,arguments)}};f.addFunction=xa;f.removeFunction=ua;f.UTF8ToString=ra;f.ALLOC_NORMAL=ba;f.allocate=aa;f.allocateUTF8OnStack=ta;var ad;Xa=function bd(){ad||cd();ad||(Xa=bd)};
function cd(){function a(){if(!ad&&(ad=!0,f.calledRun=!0,!Ma)){f.noFSInit||hc||(hc=!0,gc(),f.stdin=f.stdin,f.stdout=f.stdout,f.stderr=f.stderr,f.stdin?ic("stdin",f.stdin):Zb("/dev/tty","/dev/stdin"),f.stdout?ic("stdout",null,f.stdout):Zb("/dev/tty","/dev/stdout"),f.stderr?ic("stderr",null,f.stderr):Zb("/dev/tty1","/dev/stderr"),la("/dev/stdin",0),la("/dev/stdout",1),la("/dev/stderr",1));Lb=!1;eb(Sa);if(f.onRuntimeInitialized)f.onRuntimeInitialized();if(f.postRun)for("function"==typeof f.postRun&&
(f.postRun=[f.postRun]);f.postRun.length;){var b=f.postRun.shift();Ta.unshift(b)}eb(Ta)}}if(!(0<G)){if(f.preRun)for("function"==typeof f.preRun&&(f.preRun=[f.preRun]);f.preRun.length;)Va();eb(Ra);0<G||(f.setStatus?(f.setStatus("Running..."),setTimeout(function(){setTimeout(function(){f.setStatus("")},1);a()},1)):a())}}if(f.preInit)for("function"==typeof f.preInit&&(f.preInit=[f.preInit]);0<f.preInit.length;)f.preInit.pop()();cd();
// The shell-pre.js and emcc-generated code goes above
return Module;
}); // The end of the promise being returned
return initSqlJsPromise;
} // The end of our initSqlJs function
// This bit below is copied almost exactly from what you get when you use the MODULARIZE=1 flag with emcc
// However, we don't want to use the emcc modularization. See shell-pre.js
if (typeof exports === 'object' && typeof module === 'object'){
module.exports = initSqlJs;
// This will allow the module to be used in ES6 or CommonJS
module.exports.default = initSqlJs;
}
else if (typeof define === 'function' && define['amd']) {
define([], function() { return initSqlJs; });
}
else if (typeof exports === 'object'){
exports["Module"] = initSqlJs;
}

BIN
static/sql-wasm.1.16.wasm Normal file

Binary file not shown.

192
static/sql-wasm.js Normal file
View File

@ -0,0 +1,192 @@
// We are modularizing this manually because the current modularize setting in Emscripten has some issues:
// https://github.com/kripken/emscripten/issues/5820
// In addition, When you use emcc's modularization, it still expects to export a global object called `Module`,
// which is able to be used/called before the WASM is loaded.
// The modularization below exports a promise that loads and resolves to the actual sql.js module.
// That way, this module can't be used before the WASM is finished loading.
// We are going to define a function that a user will call to start loading initializing our Sql.js library
// However, that function might be called multiple times, and on subsequent calls, we don't actually want it to instantiate a new instance of the Module
// Instead, we want to return the previously loaded module
// TODO: Make this not declare a global if used in the browser
var initSqlJsPromise = undefined;
var initSqlJs = function (moduleConfig) {
if (initSqlJsPromise){
return initSqlJsPromise;
}
// If we're here, we've never called this function before
initSqlJsPromise = new Promise(function (resolveModule, reject) {
// We are modularizing this manually because the current modularize setting in Emscripten has some issues:
// https://github.com/kripken/emscripten/issues/5820
// The way to affect the loading of emcc compiled modules is to create a variable called `Module` and add
// properties to it, like `preRun`, `postRun`, etc
// We are using that to get notified when the WASM has finished loading.
// Only then will we return our promise
// If they passed in a moduleConfig object, use that
// Otherwise, initialize Module to the empty object
var Module = typeof moduleConfig !== 'undefined' ? moduleConfig : {};
// EMCC only allows for a single onAbort function (not an array of functions)
// So if the user defined their own onAbort function, we remember it and call it
var originalOnAbortFunction = Module['onAbort'];
Module['onAbort'] = function (errorThatCausedAbort) {
reject(new Error(errorThatCausedAbort));
if (originalOnAbortFunction){
originalOnAbortFunction(errorThatCausedAbort);
}
};
Module['postRun'] = Module['postRun'] || [];
Module['postRun'].push(function () {
// When Emscripted calls postRun, this promise resolves with the built Module
resolveModule(Module);
});
// There is a section of code in the emcc-generated code below that looks like this:
// (Note that this is lowercase `module`)
// if (typeof module !== 'undefined') {
// module['exports'] = Module;
// }
// When that runs, it's going to overwrite our own modularization export efforts in shell-post.js!
// The only way to tell emcc not to emit it is to pass the MODULARIZE=1 or MODULARIZE_INSTANCE=1 flags,
// but that carries with it additional unnecessary baggage/bugs we don't want either.
// So, we have three options:
// 1) We undefine `module`
// 2) We remember what `module['exports']` was at the beginning of this function and we restore it later
// 3) We write a script to remove those lines of code as part of the Make process.
//
// Since those are the only lines of code that care about module, we will undefine it. It's the most straightforward
// of the options, and has the side effect of reducing emcc's efforts to modify the module if its output were to change in the future.
// That's a nice side effect since we're handling the modularization efforts ourselves
module = undefined;
// The emcc-generated code and shell-post.js code goes below,
// meaning that all of it runs inside of this promise. If anything throws an exception, our promise will abort
var f;f||=typeof Module !== 'undefined' ? Module : {};"use strict";
f.onRuntimeInitialized=function(){function a(g,l){switch(typeof l){case "boolean":mc(g,l?1:0);break;case "number":nc(g,l);break;case "string":oc(g,l,-1,-1);break;case "object":if(null===l)lb(g);else if(null!=l.length){var n=aa(l,ba);pc(g,n,l.length,-1);ca(n)}else Aa(g,"Wrong API use : tried to return a value of an unknown type ("+l+").",-1);break;default:lb(g)}}function b(g,l){for(var n=[],t=0;t<g;t+=1){var w=m(l+4*t,"i32"),z=qc(w);if(1===z||2===z)w=rc(w);else if(3===z)w=sc(w);else if(4===z){z=w;
w=tc(z);z=uc(z);for(var N=new Uint8Array(w),L=0;L<w;L+=1)N[L]=p[z+L];w=N}else w=null;n.push(w)}return n}function c(g,l){this.La=g;this.db=l;this.Ja=1;this.fb=[]}function d(g,l){this.db=l;l=da(g)+1;this.Ya=ea(l);if(null===this.Ya)throw Error("Unable to allocate memory for the SQL string");fa(g,q,this.Ya,l);this.eb=this.Ya;this.Ua=this.ib=null}function e(g){this.filename="dbfile_"+(4294967295*Math.random()>>>0);if(null!=g){var l=this.filename,n="/",t=l;n&&(n="string"==typeof n?n:ha(n),t=l?u(n+"/"+l):
n);l=ia(!0,!0);t=ja(t,(void 0!==l?l:438)&4095|32768,0);if(g){if("string"==typeof g){n=Array(g.length);for(var w=0,z=g.length;w<z;++w)n[w]=g.charCodeAt(w);g=n}ka(t,l|146);n=la(t,577);ma(n,g,0,g.length,0);na(n);ka(t,l)}}this.handleError(r(this.filename,h));this.db=m(h,"i32");ob(this.db);this.Za={};this.Na={}}var h=x(4),k=f.cwrap,r=k("sqlite3_open","number",["string","number"]),y=k("sqlite3_close_v2","number",["number"]),v=k("sqlite3_exec","number",["number","string","number","number","number"]),F=k("sqlite3_changes",
"number",["number"]),H=k("sqlite3_prepare_v2","number",["number","string","number","number","number"]),pb=k("sqlite3_sql","string",["number"]),vc=k("sqlite3_normalized_sql","string",["number"]),qb=k("sqlite3_prepare_v2","number",["number","number","number","number","number"]),wc=k("sqlite3_bind_text","number",["number","number","number","number","number"]),rb=k("sqlite3_bind_blob","number",["number","number","number","number","number"]),xc=k("sqlite3_bind_double","number",["number","number","number"]),
yc=k("sqlite3_bind_int","number",["number","number","number"]),zc=k("sqlite3_bind_parameter_index","number",["number","string"]),Ac=k("sqlite3_step","number",["number"]),Bc=k("sqlite3_errmsg","string",["number"]),Cc=k("sqlite3_column_count","number",["number"]),Dc=k("sqlite3_data_count","number",["number"]),Ec=k("sqlite3_column_double","number",["number","number"]),sb=k("sqlite3_column_text","string",["number","number"]),Fc=k("sqlite3_column_blob","number",["number","number"]),Gc=k("sqlite3_column_bytes",
"number",["number","number"]),Hc=k("sqlite3_column_type","number",["number","number"]),Ic=k("sqlite3_column_name","string",["number","number"]),Jc=k("sqlite3_reset","number",["number"]),Kc=k("sqlite3_clear_bindings","number",["number"]),Lc=k("sqlite3_finalize","number",["number"]),tb=k("sqlite3_create_function_v2","number","number string number number number number number number number".split(" ")),qc=k("sqlite3_value_type","number",["number"]),tc=k("sqlite3_value_bytes","number",["number"]),sc=k("sqlite3_value_text",
"string",["number"]),uc=k("sqlite3_value_blob","number",["number"]),rc=k("sqlite3_value_double","number",["number"]),nc=k("sqlite3_result_double","",["number","number"]),lb=k("sqlite3_result_null","",["number"]),oc=k("sqlite3_result_text","",["number","string","number","number"]),pc=k("sqlite3_result_blob","",["number","number","number","number"]),mc=k("sqlite3_result_int","",["number","number"]),Aa=k("sqlite3_result_error","",["number","string","number"]),ub=k("sqlite3_aggregate_context","number",
["number","number"]),ob=k("RegisterExtensionFunctions","number",["number"]);c.prototype.bind=function(g){if(!this.La)throw"Statement closed";this.reset();return Array.isArray(g)?this.wb(g):null!=g&&"object"===typeof g?this.xb(g):!0};c.prototype.step=function(){if(!this.La)throw"Statement closed";this.Ja=1;var g=Ac(this.La);switch(g){case 100:return!0;case 101:return!1;default:throw this.db.handleError(g);}};c.prototype.rb=function(g){null==g&&(g=this.Ja,this.Ja+=1);return Ec(this.La,g)};c.prototype.Ab=
function(g){null==g&&(g=this.Ja,this.Ja+=1);g=sb(this.La,g);if("function"!==typeof BigInt)throw Error("BigInt is not supported");return BigInt(g)};c.prototype.Bb=function(g){null==g&&(g=this.Ja,this.Ja+=1);return sb(this.La,g)};c.prototype.getBlob=function(g){null==g&&(g=this.Ja,this.Ja+=1);var l=Gc(this.La,g);g=Fc(this.La,g);for(var n=new Uint8Array(l),t=0;t<l;t+=1)n[t]=p[g+t];return n};c.prototype.get=function(g,l){l=l||{};null!=g&&this.bind(g)&&this.step();g=[];for(var n=Dc(this.La),t=0;t<n;t+=
1)switch(Hc(this.La,t)){case 1:var w=l.useBigInt?this.Ab(t):this.rb(t);g.push(w);break;case 2:g.push(this.rb(t));break;case 3:g.push(this.Bb(t));break;case 4:g.push(this.getBlob(t));break;default:g.push(null)}return g};c.prototype.getColumnNames=function(){for(var g=[],l=Cc(this.La),n=0;n<l;n+=1)g.push(Ic(this.La,n));return g};c.prototype.getAsObject=function(g,l){g=this.get(g,l);l=this.getColumnNames();for(var n={},t=0;t<l.length;t+=1)n[l[t]]=g[t];return n};c.prototype.getSQL=function(){return pb(this.La)};
c.prototype.getNormalizedSQL=function(){return vc(this.La)};c.prototype.run=function(g){null!=g&&this.bind(g);this.step();return this.reset()};c.prototype.nb=function(g,l){null==l&&(l=this.Ja,this.Ja+=1);g=oa(g);var n=aa(g,ba);this.fb.push(n);this.db.handleError(wc(this.La,l,n,g.length-1,0))};c.prototype.vb=function(g,l){null==l&&(l=this.Ja,this.Ja+=1);var n=aa(g,ba);this.fb.push(n);this.db.handleError(rb(this.La,l,n,g.length,0))};c.prototype.mb=function(g,l){null==l&&(l=this.Ja,this.Ja+=1);this.db.handleError((g===
(g|0)?yc:xc)(this.La,l,g))};c.prototype.yb=function(g){null==g&&(g=this.Ja,this.Ja+=1);rb(this.La,g,0,0,0)};c.prototype.ob=function(g,l){null==l&&(l=this.Ja,this.Ja+=1);switch(typeof g){case "string":this.nb(g,l);return;case "number":this.mb(g,l);return;case "bigint":this.nb(g.toString(),l);return;case "boolean":this.mb(g+0,l);return;case "object":if(null===g){this.yb(l);return}if(null!=g.length){this.vb(g,l);return}}throw"Wrong API use : tried to bind a value of an unknown type ("+g+").";};c.prototype.xb=
function(g){var l=this;Object.keys(g).forEach(function(n){var t=zc(l.La,n);0!==t&&l.ob(g[n],t)});return!0};c.prototype.wb=function(g){for(var l=0;l<g.length;l+=1)this.ob(g[l],l+1);return!0};c.prototype.reset=function(){this.freemem();return 0===Kc(this.La)&&0===Jc(this.La)};c.prototype.freemem=function(){for(var g;void 0!==(g=this.fb.pop());)ca(g)};c.prototype.free=function(){this.freemem();var g=0===Lc(this.La);delete this.db.Za[this.La];this.La=0;return g};d.prototype.next=function(){if(null===
this.Ya)return{done:!0};null!==this.Ua&&(this.Ua.free(),this.Ua=null);if(!this.db.db)throw this.gb(),Error("Database closed");var g=pa(),l=x(4);qa(h);qa(l);try{this.db.handleError(qb(this.db.db,this.eb,-1,h,l));this.eb=m(l,"i32");var n=m(h,"i32");if(0===n)return this.gb(),{done:!0};this.Ua=new c(n,this.db);this.db.Za[n]=this.Ua;return{value:this.Ua,done:!1}}catch(t){throw this.ib=ra(this.eb),this.gb(),t;}finally{sa(g)}};d.prototype.gb=function(){ca(this.Ya);this.Ya=null};d.prototype.getRemainingSQL=
function(){return null!==this.ib?this.ib:ra(this.eb)};"function"===typeof Symbol&&"symbol"===typeof Symbol.iterator&&(d.prototype[Symbol.iterator]=function(){return this});e.prototype.run=function(g,l){if(!this.db)throw"Database closed";if(l){g=this.prepare(g,l);try{g.step()}finally{g.free()}}else this.handleError(v(this.db,g,0,0,h));return this};e.prototype.exec=function(g,l,n){if(!this.db)throw"Database closed";var t=pa(),w=null;try{var z=ta(g),N=x(4);for(g=[];0!==m(z,"i8");){qa(h);qa(N);this.handleError(qb(this.db,
z,-1,h,N));var L=m(h,"i32");z=m(N,"i32");if(0!==L){var K=null;w=new c(L,this);for(null!=l&&w.bind(l);w.step();)null===K&&(K={columns:w.getColumnNames(),values:[]},g.push(K)),K.values.push(w.get(null,n));w.free()}}return g}catch(O){throw w&&w.free(),O;}finally{sa(t)}};e.prototype.each=function(g,l,n,t,w){"function"===typeof l&&(t=n,n=l,l=void 0);g=this.prepare(g,l);try{for(;g.step();)n(g.getAsObject(null,w))}finally{g.free()}if("function"===typeof t)return t()};e.prototype.prepare=function(g,l){qa(h);
this.handleError(H(this.db,g,-1,h,0));g=m(h,"i32");if(0===g)throw"Nothing to prepare";var n=new c(g,this);null!=l&&n.bind(l);return this.Za[g]=n};e.prototype.iterateStatements=function(g){return new d(g,this)};e.prototype["export"]=function(){Object.values(this.Za).forEach(function(l){l.free()});Object.values(this.Na).forEach(ua);this.Na={};this.handleError(y(this.db));var g=va(this.filename);this.handleError(r(this.filename,h));this.db=m(h,"i32");ob(this.db);return g};e.prototype.close=function(){null!==
this.db&&(Object.values(this.Za).forEach(function(g){g.free()}),Object.values(this.Na).forEach(ua),this.Na={},this.handleError(y(this.db)),wa("/"+this.filename),this.db=null)};e.prototype.handleError=function(g){if(0===g)return null;g=Bc(this.db);throw Error(g);};e.prototype.getRowsModified=function(){return F(this.db)};e.prototype.create_function=function(g,l){Object.prototype.hasOwnProperty.call(this.Na,g)&&(ua(this.Na[g]),delete this.Na[g]);var n=xa(function(t,w,z){w=b(w,z);try{var N=l.apply(null,
w)}catch(L){Aa(t,L,-1);return}a(t,N)},"viii");this.Na[g]=n;this.handleError(tb(this.db,g,l.length,1,0,n,0,0,0));return this};e.prototype.create_aggregate=function(g,l){var n=l.init||function(){return null},t=l.finalize||function(K){return K},w=l.step;if(!w)throw"An aggregate function must have a step function in "+g;var z={};Object.hasOwnProperty.call(this.Na,g)&&(ua(this.Na[g]),delete this.Na[g]);l=g+"__finalize";Object.hasOwnProperty.call(this.Na,l)&&(ua(this.Na[l]),delete this.Na[l]);var N=xa(function(K,
O,Ua){var X=ub(K,1);Object.hasOwnProperty.call(z,X)||(z[X]=n());O=b(O,Ua);O=[z[X]].concat(O);try{z[X]=w.apply(null,O)}catch(Nc){delete z[X],Aa(K,Nc,-1)}},"viii"),L=xa(function(K){var O=ub(K,1);try{var Ua=t(z[O])}catch(X){delete z[O];Aa(K,X,-1);return}a(K,Ua);delete z[O]},"vi");this.Na[g]=N;this.Na[l]=L;this.handleError(tb(this.db,g,w.length-1,1,0,0,N,L,0));return this};f.Database=e};
var ya=Object.assign({},f),za="./this.program",Ba="object"==typeof window,Ca="function"==typeof importScripts,Da="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,A="",Ea,Fa,Ga;
if(Da){var fs=require("fs"),Ha=require("path");A=Ca?Ha.dirname(A)+"/":__dirname+"/";Ea=(a,b)=>{a=Ia(a)?new URL(a):Ha.normalize(a);return fs.readFileSync(a,b?void 0:"utf8")};Ga=a=>{a=Ea(a,!0);a.buffer||(a=new Uint8Array(a));return a};Fa=(a,b,c,d=!0)=>{a=Ia(a)?new URL(a):Ha.normalize(a);fs.readFile(a,d?void 0:"utf8",(e,h)=>{e?c(e):b(d?h.buffer:h)})};!f.thisProgram&&1<process.argv.length&&(za=process.argv[1].replace(/\\/g,"/"));process.argv.slice(2);"undefined"!=typeof module&&(module.exports=f);f.inspect=
()=>"[Emscripten Module object]"}else if(Ba||Ca)Ca?A=self.location.href:"undefined"!=typeof document&&document.currentScript&&(A=document.currentScript.src),A=0!==A.indexOf("blob:")?A.substr(0,A.replace(/[?#].*/,"").lastIndexOf("/")+1):"",Ea=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.send(null);return b.responseText},Ca&&(Ga=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),Fa=(a,b,c)=>{var d=new XMLHttpRequest;d.open("GET",
a,!0);d.responseType="arraybuffer";d.onload=()=>{200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};var Ja=f.print||console.log.bind(console),B=f.printErr||console.error.bind(console);Object.assign(f,ya);ya=null;f.thisProgram&&(za=f.thisProgram);var Ka;f.wasmBinary&&(Ka=f.wasmBinary);"object"!=typeof WebAssembly&&C("no native wasm support detected");var La,Ma=!1,p,q,Na,D,E,Oa,Pa;
function Qa(){var a=La.buffer;f.HEAP8=p=new Int8Array(a);f.HEAP16=Na=new Int16Array(a);f.HEAPU8=q=new Uint8Array(a);f.HEAPU16=new Uint16Array(a);f.HEAP32=D=new Int32Array(a);f.HEAPU32=E=new Uint32Array(a);f.HEAPF32=Oa=new Float32Array(a);f.HEAPF64=Pa=new Float64Array(a)}var Ra=[],Sa=[],Ta=[];function Va(){var a=f.preRun.shift();Ra.unshift(a)}var G=0,Wa=null,Xa=null;
function C(a){f.onAbort?.(a);a="Aborted("+a+")";B(a);Ma=!0;throw new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");}var Ya=a=>a.startsWith("data:application/octet-stream;base64,"),Ia=a=>a.startsWith("file://"),Za;Za="sql-wasm.wasm";if(!Ya(Za)){var $a=Za;Za=f.locateFile?f.locateFile($a,A):A+$a}function ab(a){if(a==Za&&Ka)return new Uint8Array(Ka);if(Ga)return Ga(a);throw"both async and sync fetching of the wasm failed";}
function bb(a){if(!Ka&&(Ba||Ca)){if("function"==typeof fetch&&!Ia(a))return fetch(a,{credentials:"same-origin"}).then(b=>{if(!b.ok)throw"failed to load wasm binary file at '"+a+"'";return b.arrayBuffer()}).catch(()=>ab(a));if(Fa)return new Promise((b,c)=>{Fa(a,d=>b(new Uint8Array(d)),c)})}return Promise.resolve().then(()=>ab(a))}function cb(a,b,c){return bb(a).then(d=>WebAssembly.instantiate(d,b)).then(d=>d).then(c,d=>{B(`failed to asynchronously prepare wasm: ${d}`);C(d)})}
function db(a,b){var c=Za;Ka||"function"!=typeof WebAssembly.instantiateStreaming||Ya(c)||Ia(c)||Da||"function"!=typeof fetch?cb(c,a,b):fetch(c,{credentials:"same-origin"}).then(d=>WebAssembly.instantiateStreaming(d,a).then(b,function(e){B(`wasm streaming compile failed: ${e}`);B("falling back to ArrayBuffer instantiation");return cb(c,a,b)}))}var I,J,eb=a=>{for(;0<a.length;)a.shift()(f)};
function m(a,b="i8"){b.endsWith("*")&&(b="*");switch(b){case "i1":return p[a>>0];case "i8":return p[a>>0];case "i16":return Na[a>>1];case "i32":return D[a>>2];case "i64":C("to do getValue(i64) use WASM_BIGINT");case "float":return Oa[a>>2];case "double":return Pa[a>>3];case "*":return E[a>>2];default:C(`invalid type for getValue: ${b}`)}}
function qa(a){var b="i32";b.endsWith("*")&&(b="*");switch(b){case "i1":p[a>>0]=0;break;case "i8":p[a>>0]=0;break;case "i16":Na[a>>1]=0;break;case "i32":D[a>>2]=0;break;case "i64":C("to do setValue(i64) use WASM_BIGINT");case "float":Oa[a>>2]=0;break;case "double":Pa[a>>3]=0;break;case "*":E[a>>2]=0;break;default:C(`invalid type for setValue: ${b}`)}}
var fb="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0,M=(a,b,c)=>{var d=b+c;for(c=b;a[c]&&!(c>=d);)++c;if(16<c-b&&a.buffer&&fb)return fb.decode(a.subarray(b,c));for(d="";b<c;){var e=a[b++];if(e&128){var h=a[b++]&63;if(192==(e&224))d+=String.fromCharCode((e&31)<<6|h);else{var k=a[b++]&63;e=224==(e&240)?(e&15)<<12|h<<6|k:(e&7)<<18|h<<12|k<<6|a[b++]&63;65536>e?d+=String.fromCharCode(e):(e-=65536,d+=String.fromCharCode(55296|e>>10,56320|e&1023))}}else d+=String.fromCharCode(e)}return d},
ra=(a,b)=>a?M(q,a,b):"",gb=(a,b)=>{for(var c=0,d=a.length-1;0<=d;d--){var e=a[d];"."===e?a.splice(d,1):".."===e?(a.splice(d,1),c++):c&&(a.splice(d,1),c--)}if(b)for(;c;c--)a.unshift("..");return a},u=a=>{var b="/"===a.charAt(0),c="/"===a.substr(-1);(a=gb(a.split("/").filter(d=>!!d),!b).join("/"))||b||(a=".");a&&c&&(a+="/");return(b?"/":"")+a},hb=a=>{var b=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(a).slice(1);a=b[0];b=b[1];if(!a&&!b)return".";b&&=b.substr(0,b.length-1);return a+
b},ib=a=>{if("/"===a)return"/";a=u(a);a=a.replace(/\/$/,"");var b=a.lastIndexOf("/");return-1===b?a:a.substr(b+1)},jb=()=>{if("object"==typeof crypto&&"function"==typeof crypto.getRandomValues)return c=>crypto.getRandomValues(c);if(Da)try{var a=require("crypto");if(a.randomFillSync)return c=>a.randomFillSync(c);var b=a.randomBytes;return c=>(c.set(b(c.byteLength)),c)}catch(c){}C("initRandomDevice")},kb=a=>(kb=jb())(a);
function mb(){for(var a="",b=!1,c=arguments.length-1;-1<=c&&!b;c--){b=0<=c?arguments[c]:"/";if("string"!=typeof b)throw new TypeError("Arguments to path.resolve must be strings");if(!b)return"";a=b+"/"+a;b="/"===b.charAt(0)}a=gb(a.split("/").filter(d=>!!d),!b).join("/");return(b?"/":"")+a||"."}
var nb=[],da=a=>{for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=d?(b+=4,++c):b+=3}return b},fa=(a,b,c,d)=>{if(!(0<d))return 0;var e=c;d=c+d-1;for(var h=0;h<a.length;++h){var k=a.charCodeAt(h);if(55296<=k&&57343>=k){var r=a.charCodeAt(++h);k=65536+((k&1023)<<10)|r&1023}if(127>=k){if(c>=d)break;b[c++]=k}else{if(2047>=k){if(c+1>=d)break;b[c++]=192|k>>6}else{if(65535>=k){if(c+2>=d)break;b[c++]=224|k>>12}else{if(c+3>=d)break;b[c++]=240|k>>18;b[c++]=128|k>>
12&63}b[c++]=128|k>>6&63}b[c++]=128|k&63}}b[c]=0;return c-e};function oa(a,b){var c=Array(da(a)+1);a=fa(a,c,0,c.length);b&&(c.length=a);return c}var vb=[];function wb(a,b){vb[a]={input:[],output:[],Xa:b};xb(a,yb)}
var yb={open(a){var b=vb[a.node.rdev];if(!b)throw new P(43);a.tty=b;a.seekable=!1},close(a){a.tty.Xa.fsync(a.tty)},fsync(a){a.tty.Xa.fsync(a.tty)},read(a,b,c,d){if(!a.tty||!a.tty.Xa.sb)throw new P(60);for(var e=0,h=0;h<d;h++){try{var k=a.tty.Xa.sb(a.tty)}catch(r){throw new P(29);}if(void 0===k&&0===e)throw new P(6);if(null===k||void 0===k)break;e++;b[c+h]=k}e&&(a.node.timestamp=Date.now());return e},write(a,b,c,d){if(!a.tty||!a.tty.Xa.jb)throw new P(60);try{for(var e=0;e<d;e++)a.tty.Xa.jb(a.tty,b[c+
e])}catch(h){throw new P(29);}d&&(a.node.timestamp=Date.now());return e}},zb={sb(){a:{if(!nb.length){var a=null;if(Da){var b=Buffer.alloc(256),c=0,d=process.stdin.fd;try{c=fs.readSync(d,b)}catch(e){if(e.toString().includes("EOF"))c=0;else throw e;}0<c?a=b.slice(0,c).toString("utf-8"):a=null}else"undefined"!=typeof window&&"function"==typeof window.prompt?(a=window.prompt("Input: "),null!==a&&(a+="\n")):"function"==typeof readline&&(a=readline(),null!==a&&(a+="\n"));if(!a){a=null;break a}nb=oa(a,!0)}a=
nb.shift()}return a},jb(a,b){null===b||10===b?(Ja(M(a.output,0)),a.output=[]):0!=b&&a.output.push(b)},fsync(a){a.output&&0<a.output.length&&(Ja(M(a.output,0)),a.output=[])},Mb(){return{Ib:25856,Kb:5,Hb:191,Jb:35387,Gb:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},Nb(){return 0},Ob(){return[24,80]}},Ab={jb(a,b){null===b||10===b?(B(M(a.output,0)),a.output=[]):0!=b&&a.output.push(b)},fsync(a){a.output&&0<a.output.length&&(B(M(a.output,0)),a.output=[])}};
function Bb(a,b){var c=a.Ia?a.Ia.length:0;c>=b||(b=Math.max(b,c*(1048576>c?2:1.125)>>>0),0!=c&&(b=Math.max(b,256)),c=a.Ia,a.Ia=new Uint8Array(b),0<a.Ma&&a.Ia.set(c.subarray(0,a.Ma),0))}
var Q={Qa:null,Ra(){return Q.createNode(null,"/",16895,0)},createNode(a,b,c,d){if(24576===(c&61440)||4096===(c&61440))throw new P(63);Q.Qa||(Q.Qa={dir:{node:{Pa:Q.Ga.Pa,Oa:Q.Ga.Oa,lookup:Q.Ga.lookup,ab:Q.Ga.ab,rename:Q.Ga.rename,unlink:Q.Ga.unlink,rmdir:Q.Ga.rmdir,readdir:Q.Ga.readdir,symlink:Q.Ga.symlink},stream:{Ta:Q.Ha.Ta}},file:{node:{Pa:Q.Ga.Pa,Oa:Q.Ga.Oa},stream:{Ta:Q.Ha.Ta,read:Q.Ha.read,write:Q.Ha.write,lb:Q.Ha.lb,bb:Q.Ha.bb,cb:Q.Ha.cb}},link:{node:{Pa:Q.Ga.Pa,Oa:Q.Ga.Oa,readlink:Q.Ga.readlink},
stream:{}},pb:{node:{Pa:Q.Ga.Pa,Oa:Q.Ga.Oa},stream:Cb}});c=Db(a,b,c,d);R(c.mode)?(c.Ga=Q.Qa.dir.node,c.Ha=Q.Qa.dir.stream,c.Ia={}):32768===(c.mode&61440)?(c.Ga=Q.Qa.file.node,c.Ha=Q.Qa.file.stream,c.Ma=0,c.Ia=null):40960===(c.mode&61440)?(c.Ga=Q.Qa.link.node,c.Ha=Q.Qa.link.stream):8192===(c.mode&61440)&&(c.Ga=Q.Qa.pb.node,c.Ha=Q.Qa.pb.stream);c.timestamp=Date.now();a&&(a.Ia[b]=c,a.timestamp=c.timestamp);return c},Lb(a){return a.Ia?a.Ia.subarray?a.Ia.subarray(0,a.Ma):new Uint8Array(a.Ia):new Uint8Array(0)},
Ga:{Pa(a){var b={};b.dev=8192===(a.mode&61440)?a.id:1;b.ino=a.id;b.mode=a.mode;b.nlink=1;b.uid=0;b.gid=0;b.rdev=a.rdev;R(a.mode)?b.size=4096:32768===(a.mode&61440)?b.size=a.Ma:40960===(a.mode&61440)?b.size=a.link.length:b.size=0;b.atime=new Date(a.timestamp);b.mtime=new Date(a.timestamp);b.ctime=new Date(a.timestamp);b.zb=4096;b.blocks=Math.ceil(b.size/b.zb);return b},Oa(a,b){void 0!==b.mode&&(a.mode=b.mode);void 0!==b.timestamp&&(a.timestamp=b.timestamp);if(void 0!==b.size&&(b=b.size,a.Ma!=b))if(0==
b)a.Ia=null,a.Ma=0;else{var c=a.Ia;a.Ia=new Uint8Array(b);c&&a.Ia.set(c.subarray(0,Math.min(b,a.Ma)));a.Ma=b}},lookup(){throw Eb[44];},ab(a,b,c,d){return Q.createNode(a,b,c,d)},rename(a,b,c){if(R(a.mode)){try{var d=Fb(b,c)}catch(h){}if(d)for(var e in d.Ia)throw new P(55);}delete a.parent.Ia[a.name];a.parent.timestamp=Date.now();a.name=c;b.Ia[c]=a;b.timestamp=a.parent.timestamp;a.parent=b},unlink(a,b){delete a.Ia[b];a.timestamp=Date.now()},rmdir(a,b){var c=Fb(a,b),d;for(d in c.Ia)throw new P(55);delete a.Ia[b];
a.timestamp=Date.now()},readdir(a){var b=[".",".."],c;for(c of Object.keys(a.Ia))b.push(c);return b},symlink(a,b,c){a=Q.createNode(a,b,41471,0);a.link=c;return a},readlink(a){if(40960!==(a.mode&61440))throw new P(28);return a.link}},Ha:{read(a,b,c,d,e){var h=a.node.Ia;if(e>=a.node.Ma)return 0;a=Math.min(a.node.Ma-e,d);if(8<a&&h.subarray)b.set(h.subarray(e,e+a),c);else for(d=0;d<a;d++)b[c+d]=h[e+d];return a},write(a,b,c,d,e,h){b.buffer===p.buffer&&(h=!1);if(!d)return 0;a=a.node;a.timestamp=Date.now();
if(b.subarray&&(!a.Ia||a.Ia.subarray)){if(h)return a.Ia=b.subarray(c,c+d),a.Ma=d;if(0===a.Ma&&0===e)return a.Ia=b.slice(c,c+d),a.Ma=d;if(e+d<=a.Ma)return a.Ia.set(b.subarray(c,c+d),e),d}Bb(a,e+d);if(a.Ia.subarray&&b.subarray)a.Ia.set(b.subarray(c,c+d),e);else for(h=0;h<d;h++)a.Ia[e+h]=b[c+h];a.Ma=Math.max(a.Ma,e+d);return d},Ta(a,b,c){1===c?b+=a.position:2===c&&32768===(a.node.mode&61440)&&(b+=a.node.Ma);if(0>b)throw new P(28);return b},lb(a,b,c){Bb(a.node,b+c);a.node.Ma=Math.max(a.node.Ma,b+c)},
bb(a,b,c,d,e){if(32768!==(a.node.mode&61440))throw new P(43);a=a.node.Ia;if(e&2||a.buffer!==p.buffer){if(0<c||c+b<a.length)a.subarray?a=a.subarray(c,c+b):a=Array.prototype.slice.call(a,c,c+b);c=!0;b=65536*Math.ceil(b/65536);(e=Gb(65536,b))?(q.fill(0,e,e+b),b=e):b=0;if(!b)throw new P(48);p.set(a,b)}else c=!1,b=a.byteOffset;return{Db:b,ub:c}},cb(a,b,c,d){Q.Ha.write(a,b,0,d,c,!1);return 0}}},ia=(a,b)=>{var c=0;a&&(c|=365);b&&(c|=146);return c},Hb=null,Ib={},Jb=[],Kb=1,S=null,Lb=!0,P=null,Eb={};
function T(a,b={}){a=mb(a);if(!a)return{path:"",node:null};b=Object.assign({qb:!0,kb:0},b);if(8<b.kb)throw new P(32);a=a.split("/").filter(k=>!!k);for(var c=Hb,d="/",e=0;e<a.length;e++){var h=e===a.length-1;if(h&&b.parent)break;c=Fb(c,a[e]);d=u(d+"/"+a[e]);c.Va&&(!h||h&&b.qb)&&(c=c.Va.root);if(!h||b.Sa)for(h=0;40960===(c.mode&61440);)if(c=Mb(d),d=mb(hb(d),c),c=T(d,{kb:b.kb+1}).node,40<h++)throw new P(32);}return{path:d,node:c}}
function ha(a){for(var b;;){if(a===a.parent)return a=a.Ra.tb,b?"/"!==a[a.length-1]?`${a}/${b}`:a+b:a;b=b?`${a.name}/${b}`:a.name;a=a.parent}}function Nb(a,b){for(var c=0,d=0;d<b.length;d++)c=(c<<5)-c+b.charCodeAt(d)|0;return(a+c>>>0)%S.length}function Ob(a){var b=Nb(a.parent.id,a.name);if(S[b]===a)S[b]=a.Wa;else for(b=S[b];b;){if(b.Wa===a){b.Wa=a.Wa;break}b=b.Wa}}
function Fb(a,b){var c;if(c=(c=Pb(a,"x"))?c:a.Ga.lookup?0:2)throw new P(c,a);for(c=S[Nb(a.id,b)];c;c=c.Wa){var d=c.name;if(c.parent.id===a.id&&d===b)return c}return a.Ga.lookup(a,b)}function Db(a,b,c,d){a=new Qb(a,b,c,d);b=Nb(a.parent.id,a.name);a.Wa=S[b];return S[b]=a}function R(a){return 16384===(a&61440)}function Rb(a){var b=["r","w","rw"][a&3];a&512&&(b+="w");return b}
function Pb(a,b){if(Lb)return 0;if(!b.includes("r")||a.mode&292){if(b.includes("w")&&!(a.mode&146)||b.includes("x")&&!(a.mode&73))return 2}else return 2;return 0}function Sb(a,b){try{return Fb(a,b),20}catch(c){}return Pb(a,"wx")}function Tb(a,b,c){try{var d=Fb(a,b)}catch(e){return e.Ka}if(a=Pb(a,"wx"))return a;if(c){if(!R(d.mode))return 54;if(d===d.parent||"/"===ha(d))return 10}else if(R(d.mode))return 31;return 0}function Ub(){for(var a=0;4096>=a;a++)if(!Jb[a])return a;throw new P(33);}
function U(a){a=Jb[a];if(!a)throw new P(8);return a}function Vb(a,b=-1){Wb||(Wb=function(){this.$a={}},Wb.prototype={},Object.defineProperties(Wb.prototype,{object:{get(){return this.node},set(c){this.node=c}},flags:{get(){return this.$a.flags},set(c){this.$a.flags=c}},position:{get(){return this.$a.position},set(c){this.$a.position=c}}}));a=Object.assign(new Wb,a);-1==b&&(b=Ub());a.fd=b;return Jb[b]=a}var Cb={open(a){a.Ha=Ib[a.node.rdev].Ha;a.Ha.open?.(a)},Ta(){throw new P(70);}};
function xb(a,b){Ib[a]={Ha:b}}function Xb(a,b){var c="/"===b,d=!b;if(c&&Hb)throw new P(10);if(!c&&!d){var e=T(b,{qb:!1});b=e.path;e=e.node;if(e.Va)throw new P(10);if(!R(e.mode))throw new P(54);}b={type:a,Pb:{},tb:b,Cb:[]};a=a.Ra(b);a.Ra=b;b.root=a;c?Hb=a:e&&(e.Va=b,e.Ra&&e.Ra.Cb.push(b))}function ja(a,b,c){var d=T(a,{parent:!0}).node;a=ib(a);if(!a||"."===a||".."===a)throw new P(28);var e=Sb(d,a);if(e)throw new P(e);if(!d.Ga.ab)throw new P(63);return d.Ga.ab(d,a,b,c)}
function V(a,b){return ja(a,(void 0!==b?b:511)&1023|16384,0)}function Yb(a,b,c){"undefined"==typeof c&&(c=b,b=438);ja(a,b|8192,c)}function Zb(a,b){if(!mb(a))throw new P(44);var c=T(b,{parent:!0}).node;if(!c)throw new P(44);b=ib(b);var d=Sb(c,b);if(d)throw new P(d);if(!c.Ga.symlink)throw new P(63);c.Ga.symlink(c,b,a)}function $b(a){var b=T(a,{parent:!0}).node;a=ib(a);var c=Fb(b,a),d=Tb(b,a,!0);if(d)throw new P(d);if(!b.Ga.rmdir)throw new P(63);if(c.Va)throw new P(10);b.Ga.rmdir(b,a);Ob(c)}
function wa(a){var b=T(a,{parent:!0}).node;if(!b)throw new P(44);a=ib(a);var c=Fb(b,a),d=Tb(b,a,!1);if(d)throw new P(d);if(!b.Ga.unlink)throw new P(63);if(c.Va)throw new P(10);b.Ga.unlink(b,a);Ob(c)}function Mb(a){a=T(a).node;if(!a)throw new P(44);if(!a.Ga.readlink)throw new P(28);return mb(ha(a.parent),a.Ga.readlink(a))}function ac(a,b){a=T(a,{Sa:!b}).node;if(!a)throw new P(44);if(!a.Ga.Pa)throw new P(63);return a.Ga.Pa(a)}function bc(a){return ac(a,!0)}
function ka(a,b){a="string"==typeof a?T(a,{Sa:!0}).node:a;if(!a.Ga.Oa)throw new P(63);a.Ga.Oa(a,{mode:b&4095|a.mode&-4096,timestamp:Date.now()})}function cc(a,b){if(0>b)throw new P(28);a="string"==typeof a?T(a,{Sa:!0}).node:a;if(!a.Ga.Oa)throw new P(63);if(R(a.mode))throw new P(31);if(32768!==(a.mode&61440))throw new P(28);var c=Pb(a,"w");if(c)throw new P(c);a.Ga.Oa(a,{size:b,timestamp:Date.now()})}
function la(a,b,c){if(""===a)throw new P(44);if("string"==typeof b){var d={r:0,"r+":2,w:577,"w+":578,a:1089,"a+":1090}[b];if("undefined"==typeof d)throw Error(`Unknown file open mode: ${b}`);b=d}c=b&64?("undefined"==typeof c?438:c)&4095|32768:0;if("object"==typeof a)var e=a;else{a=u(a);try{e=T(a,{Sa:!(b&131072)}).node}catch(h){}}d=!1;if(b&64)if(e){if(b&128)throw new P(20);}else e=ja(a,c,0),d=!0;if(!e)throw new P(44);8192===(e.mode&61440)&&(b&=-513);if(b&65536&&!R(e.mode))throw new P(54);if(!d&&(c=
e?40960===(e.mode&61440)?32:R(e.mode)&&("r"!==Rb(b)||b&512)?31:Pb(e,Rb(b)):44))throw new P(c);b&512&&!d&&cc(e,0);b&=-131713;e=Vb({node:e,path:ha(e),flags:b,seekable:!0,position:0,Ha:e.Ha,Fb:[],error:!1});e.Ha.open&&e.Ha.open(e);!f.logReadFiles||b&1||(dc||={},a in dc||(dc[a]=1));return e}function na(a){if(null===a.fd)throw new P(8);a.hb&&(a.hb=null);try{a.Ha.close&&a.Ha.close(a)}catch(b){throw b;}finally{Jb[a.fd]=null}a.fd=null}
function ec(a,b,c){if(null===a.fd)throw new P(8);if(!a.seekable||!a.Ha.Ta)throw new P(70);if(0!=c&&1!=c&&2!=c)throw new P(28);a.position=a.Ha.Ta(a,b,c);a.Fb=[]}function fc(a,b,c,d,e){if(0>d||0>e)throw new P(28);if(null===a.fd)throw new P(8);if(1===(a.flags&2097155))throw new P(8);if(R(a.node.mode))throw new P(31);if(!a.Ha.read)throw new P(28);var h="undefined"!=typeof e;if(!h)e=a.position;else if(!a.seekable)throw new P(70);b=a.Ha.read(a,b,c,d,e);h||(a.position+=b);return b}
function ma(a,b,c,d,e){if(0>d||0>e)throw new P(28);if(null===a.fd)throw new P(8);if(0===(a.flags&2097155))throw new P(8);if(R(a.node.mode))throw new P(31);if(!a.Ha.write)throw new P(28);a.seekable&&a.flags&1024&&ec(a,0,2);var h="undefined"!=typeof e;if(!h)e=a.position;else if(!a.seekable)throw new P(70);b=a.Ha.write(a,b,c,d,e,void 0);h||(a.position+=b);return b}
function va(a){var b="binary";if("utf8"!==b&&"binary"!==b)throw Error(`Invalid encoding type "${b}"`);var c;var d=la(a,d||0);a=ac(a).size;var e=new Uint8Array(a);fc(d,e,0,a,0);"utf8"===b?c=M(e,0):"binary"===b&&(c=e);na(d);return c}function gc(){P||(P=function(a,b){this.name="ErrnoError";this.node=b;this.Eb=function(c){this.Ka=c};this.Eb(a);this.message="FS error"},P.prototype=Error(),P.prototype.constructor=P,[44].forEach(a=>{Eb[a]=new P(a);Eb[a].stack="<generic error, no stack>"}))}var hc;
function ic(a,b,c){a=u("/dev/"+a);var d=ia(!!b,!!c);jc||=64;var e=jc++<<8|0;xb(e,{open(h){h.seekable=!1},close(){c?.buffer?.length&&c(10)},read(h,k,r,y){for(var v=0,F=0;F<y;F++){try{var H=b()}catch(pb){throw new P(29);}if(void 0===H&&0===v)throw new P(6);if(null===H||void 0===H)break;v++;k[r+F]=H}v&&(h.node.timestamp=Date.now());return v},write(h,k,r,y){for(var v=0;v<y;v++)try{c(k[r+v])}catch(F){throw new P(29);}y&&(h.node.timestamp=Date.now());return v}});Yb(a,d,e)}var jc,W={},Wb,dc;
function kc(a,b,c){if("/"===b.charAt(0))return b;a=-100===a?"/":U(a).path;if(0==b.length){if(!c)throw new P(44);return a}return u(a+"/"+b)}
function lc(a,b,c){try{var d=a(b)}catch(h){if(h&&h.node&&u(b)!==u(ha(h.node)))return-54;throw h;}D[c>>2]=d.dev;D[c+4>>2]=d.mode;E[c+8>>2]=d.nlink;D[c+12>>2]=d.uid;D[c+16>>2]=d.gid;D[c+20>>2]=d.rdev;J=[d.size>>>0,(I=d.size,1<=+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[c+24>>2]=J[0];D[c+28>>2]=J[1];D[c+32>>2]=4096;D[c+36>>2]=d.blocks;a=d.atime.getTime();b=d.mtime.getTime();var e=d.ctime.getTime();J=[Math.floor(a/1E3)>>>0,(I=Math.floor(a/1E3),1<=
+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[c+40>>2]=J[0];D[c+44>>2]=J[1];E[c+48>>2]=a%1E3*1E3;J=[Math.floor(b/1E3)>>>0,(I=Math.floor(b/1E3),1<=+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[c+56>>2]=J[0];D[c+60>>2]=J[1];E[c+64>>2]=b%1E3*1E3;J=[Math.floor(e/1E3)>>>0,(I=Math.floor(e/1E3),1<=+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[c+72>>2]=J[0];
D[c+76>>2]=J[1];E[c+80>>2]=e%1E3*1E3;J=[d.ino>>>0,(I=d.ino,1<=+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[c+88>>2]=J[0];D[c+92>>2]=J[1];return 0}var Mc=void 0;function Oc(){var a=D[+Mc>>2];Mc+=4;return a}
var Pc=(a,b)=>b+2097152>>>0<4194305-!!a?(a>>>0)+4294967296*b:NaN,Qc=[0,31,60,91,121,152,182,213,244,274,305,335],Rc=[0,31,59,90,120,151,181,212,243,273,304,334],Sc=a=>{var b=da(a)+1,c=ea(b);c&&fa(a,q,c,b);return c},Tc={},Vc=()=>{if(!Uc){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:za||"./this.program"},b;for(b in Tc)void 0===Tc[b]?delete a[b]:a[b]=Tc[b];
var c=[];for(b in a)c.push(`${b}=${a[b]}`);Uc=c}return Uc},Uc,ta=a=>{var b=da(a)+1,c=x(b);fa(a,q,c,b);return c},Wc=(a,b,c,d)=>{var e={string:v=>{var F=0;null!==v&&void 0!==v&&0!==v&&(F=ta(v));return F},array:v=>{var F=x(v.length);p.set(v,F);return F}};a=f["_"+a];var h=[],k=0;if(d)for(var r=0;r<d.length;r++){var y=e[c[r]];y?(0===k&&(k=pa()),h[r]=y(d[r])):h[r]=d[r]}c=a.apply(null,h);return c=function(v){0!==k&&sa(k);return"string"===b?v?M(q,v):"":"boolean"===b?!!v:v}(c)},ba=0,aa=(a,b)=>{b=1==b?x(a.length):
ea(a.length);a.subarray||a.slice||(a=new Uint8Array(a));q.set(a,b);return b},Xc,Yc=[],Y,ua=a=>{Xc.delete(Y.get(a));Y.set(a,null);Yc.push(a)},xa=(a,b)=>{if(!Xc){Xc=new WeakMap;var c=Y.length;if(Xc)for(var d=0;d<0+c;d++){var e=Y.get(d);e&&Xc.set(e,d)}}if(c=Xc.get(a)||0)return c;if(Yc.length)c=Yc.pop();else{try{Y.grow(1)}catch(r){if(!(r instanceof RangeError))throw r;throw"Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.";}c=Y.length-1}try{Y.set(c,a)}catch(r){if(!(r instanceof TypeError))throw r;if("function"==
typeof WebAssembly.Function){d=WebAssembly.Function;e={i:"i32",j:"i64",f:"f32",d:"f64",e:"externref",p:"i32"};for(var h={parameters:[],results:"v"==b[0]?[]:[e[b[0]]]},k=1;k<b.length;++k)h.parameters.push(e[b[k]]);b=new d(h,a)}else{d=[1];e=b.slice(0,1);b=b.slice(1);h={i:127,p:127,j:126,f:125,d:124,e:111};d.push(96);k=b.length;128>k?d.push(k):d.push(k%128|128,k>>7);for(k=0;k<b.length;++k)d.push(h[b[k]]);"v"==e?d.push(0):d.push(1,h[e]);b=[0,97,115,109,1,0,0,0,1];e=d.length;128>e?b.push(e):b.push(e%128|
128,e>>7);b.push.apply(b,d);b.push(2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0);b=new WebAssembly.Module(new Uint8Array(b));b=(new WebAssembly.Instance(b,{e:{f:a}})).exports.f}Y.set(c,b)}Xc.set(a,c);return c};function Qb(a,b,c,d){a||=this;this.parent=a;this.Ra=a.Ra;this.Va=null;this.id=Kb++;this.name=b;this.mode=c;this.Ga={};this.Ha={};this.rdev=d}
Object.defineProperties(Qb.prototype,{read:{get:function(){return 365===(this.mode&365)},set:function(a){a?this.mode|=365:this.mode&=-366}},write:{get:function(){return 146===(this.mode&146)},set:function(a){a?this.mode|=146:this.mode&=-147}}});gc();S=Array(4096);Xb(Q,"/");V("/tmp");V("/home");V("/home/web_user");
(function(){V("/dev");xb(259,{read:()=>0,write:(d,e,h,k)=>k});Yb("/dev/null",259);wb(1280,zb);wb(1536,Ab);Yb("/dev/tty",1280);Yb("/dev/tty1",1536);var a=new Uint8Array(1024),b=0,c=()=>{0===b&&(b=kb(a).byteLength);return a[--b]};ic("random",c);ic("urandom",c);V("/dev/shm");V("/dev/shm/tmp")})();
(function(){V("/proc");var a=V("/proc/self");V("/proc/self/fd");Xb({Ra(){var b=Db(a,"fd",16895,73);b.Ga={lookup(c,d){var e=U(+d);c={parent:null,Ra:{tb:"fake"},Ga:{readlink:()=>e.path}};return c.parent=c}};return b}},"/proc/self/fd")})();
var $c={a:(a,b,c,d)=>{C(`Assertion failed: ${a?M(q,a):""}, at: `+[b?b?M(q,b):"":"unknown filename",c,d?d?M(q,d):"":"unknown function"])},h:function(a,b){try{return a=a?M(q,a):"",ka(a,b),0}catch(c){if("undefined"==typeof W||"ErrnoError"!==c.name)throw c;return-c.Ka}},H:function(a,b,c){try{b=b?M(q,b):"";b=kc(a,b);if(c&-8)return-28;var d=T(b,{Sa:!0}).node;if(!d)return-44;a="";c&4&&(a+="r");c&2&&(a+="w");c&1&&(a+="x");return a&&Pb(d,a)?-2:0}catch(e){if("undefined"==typeof W||"ErrnoError"!==e.name)throw e;
return-e.Ka}},i:function(a,b){try{var c=U(a);ka(c.node,b);return 0}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return-d.Ka}},g:function(a){try{var b=U(a).node;var c="string"==typeof b?T(b,{Sa:!0}).node:b;if(!c.Ga.Oa)throw new P(63);c.Ga.Oa(c,{timestamp:Date.now()});return 0}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return-d.Ka}},b:function(a,b,c){Mc=c;try{var d=U(a);switch(b){case 0:var e=Oc();if(0>e)return-28;for(;Jb[e];)e++;return Vb(d,e).fd;case 1:case 2:return 0;
case 3:return d.flags;case 4:return e=Oc(),d.flags|=e,0;case 5:return e=Oc(),Na[e+0>>1]=2,0;case 6:case 7:return 0;case 16:case 8:return-28;case 9:return D[Zc()>>2]=28,-1;default:return-28}}catch(h){if("undefined"==typeof W||"ErrnoError"!==h.name)throw h;return-h.Ka}},f:function(a,b){try{var c=U(a);return lc(ac,c.path,b)}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return-d.Ka}},n:function(a,b,c){b=Pc(b,c);try{if(isNaN(b))return 61;var d=U(a);if(0===(d.flags&2097155))throw new P(28);
cc(d.node,b);return 0}catch(e){if("undefined"==typeof W||"ErrnoError"!==e.name)throw e;return-e.Ka}},C:function(a,b){try{if(0===b)return-28;var c=da("/")+1;if(b<c)return-68;fa("/",q,a,b);return c}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return-d.Ka}},F:function(a,b){try{return a=a?M(q,a):"",lc(bc,a,b)}catch(c){if("undefined"==typeof W||"ErrnoError"!==c.name)throw c;return-c.Ka}},z:function(a,b,c){try{return b=b?M(q,b):"",b=kc(a,b),b=u(b),"/"===b[b.length-1]&&(b=b.substr(0,
b.length-1)),V(b,c),0}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return-d.Ka}},E:function(a,b,c,d){try{b=b?M(q,b):"";var e=d&256;b=kc(a,b,d&4096);return lc(e?bc:ac,b,c)}catch(h){if("undefined"==typeof W||"ErrnoError"!==h.name)throw h;return-h.Ka}},y:function(a,b,c,d){Mc=d;try{b=b?M(q,b):"";b=kc(a,b);var e=d?Oc():0;return la(b,c,e).fd}catch(h){if("undefined"==typeof W||"ErrnoError"!==h.name)throw h;return-h.Ka}},w:function(a,b,c,d){try{b=b?M(q,b):"";b=kc(a,b);if(0>=d)return-28;
var e=Mb(b),h=Math.min(d,da(e)),k=p[c+h];fa(e,q,c,d+1);p[c+h]=k;return h}catch(r){if("undefined"==typeof W||"ErrnoError"!==r.name)throw r;return-r.Ka}},v:function(a){try{return a=a?M(q,a):"",$b(a),0}catch(b){if("undefined"==typeof W||"ErrnoError"!==b.name)throw b;return-b.Ka}},G:function(a,b){try{return a=a?M(q,a):"",lc(ac,a,b)}catch(c){if("undefined"==typeof W||"ErrnoError"!==c.name)throw c;return-c.Ka}},r:function(a,b,c){try{return b=b?M(q,b):"",b=kc(a,b),0===c?wa(b):512===c?$b(b):C("Invalid flags passed to unlinkat"),
0}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return-d.Ka}},q:function(a,b,c){try{b=b?M(q,b):"";b=kc(a,b,!0);if(c){var d=E[c>>2]+4294967296*D[c+4>>2],e=D[c+8>>2];h=1E3*d+e/1E6;c+=16;d=E[c>>2]+4294967296*D[c+4>>2];e=D[c+8>>2];k=1E3*d+e/1E6}else var h=Date.now(),k=h;a=h;var r=T(b,{Sa:!0}).node;r.Ga.Oa(r,{timestamp:Math.max(a,k)});return 0}catch(y){if("undefined"==typeof W||"ErrnoError"!==y.name)throw y;return-y.Ka}},l:function(a,b,c){a=new Date(1E3*Pc(a,b));D[c>>2]=a.getSeconds();
D[c+4>>2]=a.getMinutes();D[c+8>>2]=a.getHours();D[c+12>>2]=a.getDate();D[c+16>>2]=a.getMonth();D[c+20>>2]=a.getFullYear()-1900;D[c+24>>2]=a.getDay();b=a.getFullYear();D[c+28>>2]=(0!==b%4||0===b%100&&0!==b%400?Rc:Qc)[a.getMonth()]+a.getDate()-1|0;D[c+36>>2]=-(60*a.getTimezoneOffset());b=(new Date(a.getFullYear(),6,1)).getTimezoneOffset();var d=(new Date(a.getFullYear(),0,1)).getTimezoneOffset();D[c+32>>2]=(b!=d&&a.getTimezoneOffset()==Math.min(d,b))|0},j:function(a,b,c,d,e,h,k,r){e=Pc(e,h);try{if(isNaN(e))return 61;
var y=U(d);if(0!==(b&2)&&0===(c&2)&&2!==(y.flags&2097155))throw new P(2);if(1===(y.flags&2097155))throw new P(2);if(!y.Ha.bb)throw new P(43);var v=y.Ha.bb(y,a,e,b,c);var F=v.Db;D[k>>2]=v.ub;E[r>>2]=F;return 0}catch(H){if("undefined"==typeof W||"ErrnoError"!==H.name)throw H;return-H.Ka}},k:function(a,b,c,d,e,h,k){h=Pc(h,k);try{if(isNaN(h))return 61;var r=U(e);if(c&2){if(32768!==(r.node.mode&61440))throw new P(43);if(!(d&2)){var y=q.slice(a,a+b);r.Ha.cb&&r.Ha.cb(r,y,h,b,d)}}}catch(v){if("undefined"==
typeof W||"ErrnoError"!==v.name)throw v;return-v.Ka}},s:(a,b,c)=>{function d(y){return(y=y.toTimeString().match(/\(([A-Za-z ]+)\)$/))?y[1]:"GMT"}var e=(new Date).getFullYear(),h=new Date(e,0,1),k=new Date(e,6,1);e=h.getTimezoneOffset();var r=k.getTimezoneOffset();E[a>>2]=60*Math.max(e,r);D[b>>2]=Number(e!=r);a=d(h);b=d(k);a=Sc(a);b=Sc(b);r<e?(E[c>>2]=a,E[c+4>>2]=b):(E[c>>2]=b,E[c+4>>2]=a)},d:()=>Date.now(),t:()=>2147483648,c:()=>performance.now(),o:a=>{var b=q.length;a>>>=0;if(2147483648<a)return!1;
for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);var e=Math;d=Math.max(a,d);a:{e=(e.min.call(e,2147483648,d+(65536-d%65536)%65536)-La.buffer.byteLength+65535)/65536;try{La.grow(e);Qa();var h=1;break a}catch(k){}h=void 0}if(h)return!0}return!1},A:(a,b)=>{var c=0;Vc().forEach((d,e)=>{var h=b+c;e=E[a+4*e>>2]=h;for(h=0;h<d.length;++h)p[e++>>0]=d.charCodeAt(h);p[e>>0]=0;c+=d.length+1});return 0},B:(a,b)=>{var c=Vc();E[a>>2]=c.length;var d=0;c.forEach(e=>d+=e.length+1);E[b>>2]=d;return 0},
e:function(a){try{var b=U(a);na(b);return 0}catch(c){if("undefined"==typeof W||"ErrnoError"!==c.name)throw c;return c.Ka}},p:function(a,b){try{var c=U(a);p[b>>0]=c.tty?2:R(c.mode)?3:40960===(c.mode&61440)?7:4;Na[b+2>>1]=0;J=[0,(I=0,1<=+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[b+8>>2]=J[0];D[b+12>>2]=J[1];J=[0,(I=0,1<=+Math.abs(I)?0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[b+16>>2]=J[0];D[b+20>>2]=J[1];
return 0}catch(d){if("undefined"==typeof W||"ErrnoError"!==d.name)throw d;return d.Ka}},x:function(a,b,c,d){try{a:{var e=U(a);a=b;for(var h,k=b=0;k<c;k++){var r=E[a>>2],y=E[a+4>>2];a+=8;var v=fc(e,p,r,y,h);if(0>v){var F=-1;break a}b+=v;if(v<y)break;"undefined"!==typeof h&&(h+=v)}F=b}E[d>>2]=F;return 0}catch(H){if("undefined"==typeof W||"ErrnoError"!==H.name)throw H;return H.Ka}},m:function(a,b,c,d,e){b=Pc(b,c);try{if(isNaN(b))return 61;var h=U(a);ec(h,b,d);J=[h.position>>>0,(I=h.position,1<=+Math.abs(I)?
0<I?+Math.floor(I/4294967296)>>>0:~~+Math.ceil((I-+(~~I>>>0))/4294967296)>>>0:0)];D[e>>2]=J[0];D[e+4>>2]=J[1];h.hb&&0===b&&0===d&&(h.hb=null);return 0}catch(k){if("undefined"==typeof W||"ErrnoError"!==k.name)throw k;return k.Ka}},D:function(a){try{var b=U(a);return b.Ha?.fsync?b.Ha.fsync(b):0}catch(c){if("undefined"==typeof W||"ErrnoError"!==c.name)throw c;return c.Ka}},u:function(a,b,c,d){try{a:{var e=U(a);a=b;for(var h,k=b=0;k<c;k++){var r=E[a>>2],y=E[a+4>>2];a+=8;var v=ma(e,p,r,y,h);if(0>v){var F=
-1;break a}b+=v;"undefined"!==typeof h&&(h+=v)}F=b}E[d>>2]=F;return 0}catch(H){if("undefined"==typeof W||"ErrnoError"!==H.name)throw H;return H.Ka}}},Z=function(){function a(c){Z=c.exports;La=Z.I;Qa();Y=Z.Aa;Sa.unshift(Z.J);G--;f.monitorRunDependencies?.(G);0==G&&(null!==Wa&&(clearInterval(Wa),Wa=null),Xa&&(c=Xa,Xa=null,c()));return Z}var b={a:$c};G++;f.monitorRunDependencies?.(G);if(f.instantiateWasm)try{return f.instantiateWasm(b,a)}catch(c){return B(`Module.instantiateWasm callback failed with error: ${c}`),
!1}db(b,function(c){a(c.instance)});return{}}();f._sqlite3_free=a=>(f._sqlite3_free=Z.K)(a);f._sqlite3_value_text=a=>(f._sqlite3_value_text=Z.L)(a);var Zc=()=>(Zc=Z.M)();f._sqlite3_prepare_v2=(a,b,c,d,e)=>(f._sqlite3_prepare_v2=Z.N)(a,b,c,d,e);f._sqlite3_step=a=>(f._sqlite3_step=Z.O)(a);f._sqlite3_reset=a=>(f._sqlite3_reset=Z.P)(a);f._sqlite3_exec=(a,b,c,d,e)=>(f._sqlite3_exec=Z.Q)(a,b,c,d,e);f._sqlite3_finalize=a=>(f._sqlite3_finalize=Z.R)(a);
f._sqlite3_column_name=(a,b)=>(f._sqlite3_column_name=Z.S)(a,b);f._sqlite3_column_text=(a,b)=>(f._sqlite3_column_text=Z.T)(a,b);f._sqlite3_column_type=(a,b)=>(f._sqlite3_column_type=Z.U)(a,b);f._sqlite3_errmsg=a=>(f._sqlite3_errmsg=Z.V)(a);f._sqlite3_clear_bindings=a=>(f._sqlite3_clear_bindings=Z.W)(a);f._sqlite3_value_blob=a=>(f._sqlite3_value_blob=Z.X)(a);f._sqlite3_value_bytes=a=>(f._sqlite3_value_bytes=Z.Y)(a);f._sqlite3_value_double=a=>(f._sqlite3_value_double=Z.Z)(a);
f._sqlite3_value_int=a=>(f._sqlite3_value_int=Z._)(a);f._sqlite3_value_type=a=>(f._sqlite3_value_type=Z.$)(a);f._sqlite3_result_blob=(a,b,c,d)=>(f._sqlite3_result_blob=Z.aa)(a,b,c,d);f._sqlite3_result_double=(a,b)=>(f._sqlite3_result_double=Z.ba)(a,b);f._sqlite3_result_error=(a,b,c)=>(f._sqlite3_result_error=Z.ca)(a,b,c);f._sqlite3_result_int=(a,b)=>(f._sqlite3_result_int=Z.da)(a,b);f._sqlite3_result_int64=(a,b,c)=>(f._sqlite3_result_int64=Z.ea)(a,b,c);
f._sqlite3_result_null=a=>(f._sqlite3_result_null=Z.fa)(a);f._sqlite3_result_text=(a,b,c,d)=>(f._sqlite3_result_text=Z.ga)(a,b,c,d);f._sqlite3_aggregate_context=(a,b)=>(f._sqlite3_aggregate_context=Z.ha)(a,b);f._sqlite3_column_count=a=>(f._sqlite3_column_count=Z.ia)(a);f._sqlite3_data_count=a=>(f._sqlite3_data_count=Z.ja)(a);f._sqlite3_column_blob=(a,b)=>(f._sqlite3_column_blob=Z.ka)(a,b);f._sqlite3_column_bytes=(a,b)=>(f._sqlite3_column_bytes=Z.la)(a,b);
f._sqlite3_column_double=(a,b)=>(f._sqlite3_column_double=Z.ma)(a,b);f._sqlite3_bind_blob=(a,b,c,d,e)=>(f._sqlite3_bind_blob=Z.na)(a,b,c,d,e);f._sqlite3_bind_double=(a,b,c)=>(f._sqlite3_bind_double=Z.oa)(a,b,c);f._sqlite3_bind_int=(a,b,c)=>(f._sqlite3_bind_int=Z.pa)(a,b,c);f._sqlite3_bind_text=(a,b,c,d,e)=>(f._sqlite3_bind_text=Z.qa)(a,b,c,d,e);f._sqlite3_bind_parameter_index=(a,b)=>(f._sqlite3_bind_parameter_index=Z.ra)(a,b);f._sqlite3_sql=a=>(f._sqlite3_sql=Z.sa)(a);
f._sqlite3_normalized_sql=a=>(f._sqlite3_normalized_sql=Z.ta)(a);f._sqlite3_changes=a=>(f._sqlite3_changes=Z.ua)(a);f._sqlite3_close_v2=a=>(f._sqlite3_close_v2=Z.va)(a);f._sqlite3_create_function_v2=(a,b,c,d,e,h,k,r,y)=>(f._sqlite3_create_function_v2=Z.wa)(a,b,c,d,e,h,k,r,y);f._sqlite3_open=(a,b)=>(f._sqlite3_open=Z.xa)(a,b);var ea=f._malloc=a=>(ea=f._malloc=Z.ya)(a),ca=f._free=a=>(ca=f._free=Z.za)(a);f._RegisterExtensionFunctions=a=>(f._RegisterExtensionFunctions=Z.Ba)(a);
var Gb=(a,b)=>(Gb=Z.Ca)(a,b),pa=()=>(pa=Z.Da)(),sa=a=>(sa=Z.Ea)(a),x=a=>(x=Z.Fa)(a);f.stackAlloc=x;f.stackSave=pa;f.stackRestore=sa;f.cwrap=(a,b,c,d)=>{var e=!c||c.every(h=>"number"===h||"boolean"===h);return"string"!==b&&e&&!d?f["_"+a]:function(){return Wc(a,b,c,arguments)}};f.addFunction=xa;f.removeFunction=ua;f.UTF8ToString=ra;f.ALLOC_NORMAL=ba;f.allocate=aa;f.allocateUTF8OnStack=ta;var ad;Xa=function bd(){ad||cd();ad||(Xa=bd)};
function cd(){function a(){if(!ad&&(ad=!0,f.calledRun=!0,!Ma)){f.noFSInit||hc||(hc=!0,gc(),f.stdin=f.stdin,f.stdout=f.stdout,f.stderr=f.stderr,f.stdin?ic("stdin",f.stdin):Zb("/dev/tty","/dev/stdin"),f.stdout?ic("stdout",null,f.stdout):Zb("/dev/tty","/dev/stdout"),f.stderr?ic("stderr",null,f.stderr):Zb("/dev/tty1","/dev/stderr"),la("/dev/stdin",0),la("/dev/stdout",1),la("/dev/stderr",1));Lb=!1;eb(Sa);if(f.onRuntimeInitialized)f.onRuntimeInitialized();if(f.postRun)for("function"==typeof f.postRun&&
(f.postRun=[f.postRun]);f.postRun.length;){var b=f.postRun.shift();Ta.unshift(b)}eb(Ta)}}if(!(0<G)){if(f.preRun)for("function"==typeof f.preRun&&(f.preRun=[f.preRun]);f.preRun.length;)Va();eb(Ra);0<G||(f.setStatus?(f.setStatus("Running..."),setTimeout(function(){setTimeout(function(){f.setStatus("")},1);a()},1)):a())}}if(f.preInit)for("function"==typeof f.preInit&&(f.preInit=[f.preInit]);0<f.preInit.length;)f.preInit.pop()();cd();
// The shell-pre.js and emcc-generated code goes above
return Module;
}); // The end of the promise being returned
return initSqlJsPromise;
} // The end of our initSqlJs function
// This bit below is copied almost exactly from what you get when you use the MODULARIZE=1 flag with emcc
// However, we don't want to use the emcc modularization. See shell-pre.js
if (typeof exports === 'object' && typeof module === 'object'){
module.exports = initSqlJs;
// This will allow the module to be used in ES6 or CommonJS
module.exports.default = initSqlJs;
}
else if (typeof define === 'function' && define['amd']) {
define([], function() { return initSqlJs; });
}
else if (typeof exports === 'object'){
exports["Module"] = initSqlJs;
}

BIN
static/sql-wasm.wasm Normal file

Binary file not shown.

41
static/styles.css Normal file
View File

@ -0,0 +1,41 @@
/* 기본 커스텀 스타일: PicoCSS 위에 얹기 */
:root{
--brand: #2563eb;
--bg-elev: color-mix(in oklab, var(--pico-background-color), black 6%);
}
html, body { height: 100%; }
body { background: var(--pico-background-color); }
.tabs, .subtabs { display:flex; gap:.5rem; margin-bottom: .75rem; }
.tabs.seg, .subtabs.seg{ background: var(--bg-elev); padding:.25rem; border-radius: .8rem; box-shadow: inset 0 1px 0 rgba(255,255,255,.06), inset 0 -1px 0 rgba(0,0,0,.25); }
.tab-button { border: 0; background: transparent; padding:.5rem .9rem; border-radius: .6rem; transition: all .15s ease; }
.tab-button[aria-selected="true"]{ background: var(--brand); color:white; box-shadow: 0 2px 8px color-mix(in oklab, var(--brand), black 40%); }
.tab-button[aria-selected="false"]{ color: #111; }
[data-theme="dark"] .tab-button[aria-selected="false"]{ color: #d1d5db; }
.tab-button.small{ padding:.35rem .7rem; font-size:.95rem; }
.grid{ display:grid; grid-template-columns: 1fr; gap: .75rem; }
@media (min-width: 720px){
.grid{ grid-template-columns: 1fr 1fr; }
}
.card-list .item{ cursor:pointer; border:1px solid var(--pico-muted-border-color); padding:.9rem; border-radius:.9rem; background: var(--bg-elev); }
.card-list .item header{ display:flex; align-items:baseline; gap:.75rem; }
.card-list .item .code{ font-weight:700; min-width:116px; color: var(--brand); }
.card-list .item .title{ font-weight:600; }
.card-list .item:hover{ border-color: var(--brand); box-shadow: 0 4px 20px rgba(0,0,0,.35); transform: translateY(-1px); }
.modal-card header{ display:flex; justify-content:space-between; align-items:center; gap:1rem; }
.modal-card .content{ max-height:min(68vh, 600px); overflow:auto; }
.prewrap{ white-space:pre-wrap; }
dialog::backdrop{ backdrop-filter: blur(2px); }
dialog{ border: none; border-radius: 12px; padding: 0; }
.modal-card{ padding: 1rem 1.25rem; }
/* iOS PWA safe-area */
:root { --safe-top: env(safe-area-inset-top); --safe-bottom: env(safe-area-inset-bottom); }
body { padding-bottom: var(--safe-bottom); }

42
static/sw.js Normal file
View File

@ -0,0 +1,42 @@
const CACHE_NAME = 'faultcodes-v1';
const ASSETS = [
'/',
'/static/styles.css',
'/static/app.js',
'/static/manifest.webmanifest'
];
self.addEventListener('install', (e) => {
e.waitUntil((async () => {
const cache = await caches.open(CACHE_NAME);
await cache.addAll(ASSETS);
})());
self.skipWaiting();
});
self.addEventListener('activate', (e) => {
e.waitUntil((async () => {
const keys = await caches.keys();
await Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)));
})());
self.clients.claim();
});
self.addEventListener('fetch', (e) => {
const req = e.request;
if (req.method !== 'GET') return;
e.respondWith((async () => {
const cache = await caches.open(CACHE_NAME);
const cached = await cache.match(req);
if (cached) return cached;
try {
const fresh = await fetch(req);
if (fresh && fresh.ok) cache.put(req, fresh.clone());
return fresh;
} catch {
return cached || Response.error();
}
})());
});

60
templates/index.html Normal file
View File

@ -0,0 +1,60 @@
<!doctype html>
<html lang="ko" data-theme="auto">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>{{ app_name }}</title>
<link rel="icon" href="data:,">
<link rel="manifest" href="/static/manifest.webmanifest">
<meta name="theme-color" content="#0f172a">
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@2.0.6/css/pico.min.css">
<link rel="stylesheet" href="/static/styles.css">
<script src="https://unpkg.com/htmx.org@1.9.12"></script>
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
<script defer src="/static/app.js"></script>
</head>
<body>
<header class="container">
<nav>
<ul>
<li><strong>{{ app_name }}</strong></li>
</ul>
<ul>
<li>
<button class="contrast" id="themeToggle" aria-label="테마 전환">🌙</button>
</li>
</ul>
</nav>
</header>
<main class="container">
<div class="tabs seg" id="topSections" role="tablist">
<button class="tab-button" aria-selected="true"
hx-get="/sb?section=fault"
hx-include="#sbManufacturer"
hx-target="#tabPanel" hx-swap="innerHTML">고장코드</button>
<button class="tab-button" aria-selected="false"
hx-get="/sb?section=tcms"
hx-include="#sbManufacturer"
hx-target="#tabPanel" hx-swap="innerHTML">TCMS 코드</button>
<button class="tab-button" aria-selected="false"
hx-get="/sb?section=mmicode"
hx-include="#sbManufacturer"
hx-target="#tabPanel" hx-swap="innerHTML">MMI 코드</button>
<button class="tab-button" aria-selected="false"
hx-get="/sb?section=emergency"
hx-include="#sbManufacturer"
hx-target="#tabPanel" hx-swap="innerHTML">응급조치요령</button>
</div>
<section id="tabPanel" hx-get="/sb" hx-trigger="load" hx-swap="innerHTML">
<!-- 제조사 탭 컨텐츠 로드 -->
</section>
</main>
<dialog id="modal"></dialog>
</body>
</html>

View File

@ -0,0 +1,18 @@
<article class="modal-card">
<header>
<h3>[{{ row.code }}] {{ row.title }}</h3>
<button class="outline" _="on click call document.getElementById('modal').close() then put '' into #modal">닫기</button>
</header>
<section class="content">
<p><strong>제조사</strong>: {{ row.manufacturer_name }}{% if row.category_name %} · <strong>타입</strong>: {{ row.category_name }}{% endif %}</p>
<hr>
<h5>고장 상세</h5>
<p class="prewrap">{{ row.details or '—' }}</p>
<h5>반응</h5>
<p class="prewrap">{{ row.action or '—' }}</p>
<h5>검지 조건</h5>
<p class="prewrap">{{ row.condition or '—' }}</p>
</section>
</article>

View File

@ -0,0 +1,19 @@
<div class="list">
{% if rows %}
{% for r in rows %}
<article class="item" hx-get="/faults/{{ r.id }}" hx-target="#modal" hx-swap="innerHTML show:top">
<header>
<div class="code">{{ r.code }}</div>
<div class="title">{{ r.title }}</div>
</header>
<footer>
<small>{{ r.category_name or '분류없음' }}</small>
</footer>
</article>
{% endfor %}
{% else %}
<p>검색 결과가 없습니다.</p>
{% endif %}
</div>

View File

@ -0,0 +1,55 @@
<section _="on load set #currentSectionRoot.value to '{{ section }}' then if '{{ section }}' != 'fault' then call (function(){ const btn = document.querySelector(`#subtabs .tab-button[data-section='{{ section }}']`); if(btn){ document.querySelectorAll('#subtabs .tab-button').forEach(x=>x.removeAttribute('aria-selected')); btn.setAttribute('aria-selected','true'); } })() end">
<div class="subtabs seg" id="subtabs" role="tablist">
<button class="tab-button small" data-section="fault" aria-selected="{{ 'true' if section=='fault' else 'false' }}"
hx-get="/faults/list?manufacturer={{ manufacturer.slug }}&section=fault"
hx-target="#faultList"
_="on click call document.querySelectorAll('#subtabs .tab-button').forEach(x => x.removeAttribute('aria-selected')); then me.setAttribute('aria-selected','true'); then set #currentSection.value to 'fault'; then set #currentSectionRoot.value to 'fault'">
고장코드
</button>
<button class="tab-button small" data-section="tcms" aria-selected="{{ 'true' if section=='tcms' else 'false' }}"
hx-get="/faults/list?manufacturer={{ manufacturer.slug }}&section=tcms"
hx-target="#faultList"
_="on click call document.querySelectorAll('#subtabs .tab-button').forEach(x => x.removeAttribute('aria-selected')); then me.setAttribute('aria-selected','true'); then set #currentSection.value to 'tcms'; then set #currentSectionRoot.value to 'tcms'">
TCMS 코드
</button>
<button class="tab-button small" data-section="emergency" aria-selected="{{ 'true' if section=='emergency' else 'false' }}"
hx-get="/faults/list?manufacturer={{ manufacturer.slug }}&section=emergency"
hx-target="#faultList"
_="on click call document.querySelectorAll('#subtabs .tab-button').forEach(x => x.removeAttribute('aria-selected')); then me.setAttribute('aria-selected','true'); then set #currentSection.value to 'emergency'; then set #currentSectionRoot.value to 'emergency'">
응급조치요령
</button>
</div>
<div class="grid">
<div>
<label for="categorySelect">장치분류 선택</label>
<select id="categorySelect"
hx-get="/faults/list?manufacturer={{ manufacturer.slug }}"
hx-include="#searchBox, #currentSection"
hx-target="#faultList"
hx-params="*"
hx-trigger="change">
<option value="">전체</option>
{% for c in categories|sort(attribute='name') %}
<option value="{{ c.id }}">{{ c.name }}</option>
{% endfor %}
</select>
</div>
<div>
<label for="searchBox">검색어</label>
<input id="searchBox" name="q" placeholder="검색어 입력"
hx-get="/faults/list?manufacturer={{ manufacturer.slug }}"
hx-trigger="keyup changed delay:200ms, search"
hx-target="#faultList"
hx-include="#categorySelect, #currentSection"
>
<input type="hidden" id="currentSection" name="section" value="fault">
</div>
</div>
<div id="faultList" class="card-list" hx-get="/faults/list?manufacturer={{ manufacturer.slug }}&section={{ section }}" hx-trigger="load">
<!-- 리스트 -->
</div>
</section>

View File

@ -0,0 +1,13 @@
<article class="card">
<header>
<strong>Supabase 연결 오류</strong>
</header>
<p class="prewrap">{{ error_message }}</p>
{% if supabase_url %}
<p>
<small>확인: {{ supabase_url }} 가 내부망에서 접근 가능한지 확인해 주세요.</small>
</p>
{% endif %}
</article>

View File

@ -0,0 +1,33 @@
<article class="modal-card">
<header>
<h3>[{{ row.f_code }}] {{ row.f_name }}</h3>
<button class="outline" _="on click call document.getElementById('modal').close() then put '' into #modal">닫기</button>
</header>
<section class="content">
<p>
<strong>제작사</strong>: {{ row.manufacturer or '—' }}
{% if row.device %} · <strong>장치</strong>: {{ row.device }}{% endif %}
{% if row.car_type %} · <strong>호차</strong>: {{ row.car_type }}{% endif %}
{% if row.car_id %} · <strong>차량분류</strong>: {{ row.car_id }}{% endif %}
</p>
<hr>
<h5>고장등급(리스트)</h5>
<p class="prewrap">{{ row.f_class or '—' }}</p>
<h5>고장등급(해설)</h5>
<p class="prewrap">{{ row.grade or '—' }}</p>
<h5>고장상세</h5>
<p class="prewrap">{{ row.fault_detail or '—' }}</p>
<h5>고장반응</h5>
<p class="prewrap">{{ row.fault_reaction or '—' }}</p>
<h5>검지조건</h5>
<p class="prewrap">{{ row.fault_detection or '—' }}</p>
<h5>소거조건</h5>
<p class="prewrap">{{ row.fault_clear or '—' }}</p>
<h5>조치방법</h5>
<p class="prewrap">{{ row.fault_action or '—' }}</p>
<h5>회로도면</h5>
<p class="prewrap">{{ row.fault_schematics or '—' }}</p>
</section>
</article>

View File

@ -0,0 +1,24 @@
<div class="list" {% if page > 0 %} style="display:contents;" {% endif %}>
{% for r in rows %}
<article class="item" hx-get="/sb/faults/{{ r.f_code }}" hx-target="#modal" hx-swap="innerHTML show:top">
<header>
<div class="code">{{ r.f_code }}</div>
<div class="title">{{ r.f_name }} {% if r.fault_detail %}<span title="고장상세 있음">🔎</span>{% endif %}</div>
</header>
<footer>
<small>{{ r.manufacturer or '—' }}{% if r.device %} · {{ r.device }}{% endif %}{% if r.car_type %} · {{ r.car_type }}{% endif %}{% if r.alias_name %} · {{ r.alias_name }}{% endif %}</small>
</footer>
</article>
{% endfor %}
</div>
{% if rows|length >= page_size %}
<div class="load-more-trigger"
hx-get="/sb/faults/list?page={{ page + 1 }}&{{ query_params_string }}"
hx-swap="outerHTML"
hx-trigger="revealed">
<article class="item placeholder">Loading...</article>
</div>
{% endif %}

View File

@ -0,0 +1,105 @@
<section>
<div class="filter-row" style="display: flex; flex-wrap: wrap; gap: 0.7rem; align-items: end; margin-bottom: 1rem;">
<div style="flex: 1; min-width:160px;">
<label for="sbManufacturer">제작사</label>
<select id="sbManufacturer" name="manufacturer" hx-get="/sb" hx-include="this" hx-target="#tabPanel" hx-params="*" hx-trigger="change">
<option value="" {% if selected_manufacturer=='' %}selected{% endif %}>전체</option>
{% for m in manufacturers %}
<option value="{{ m }}" {% if m==selected_manufacturer %}selected{% endif %}>{{ m }}</option>
{% endfor %}
</select>
</div>
{% if section == "tcms" %}
<div style="flex: 1; min-width:140px;">
<label for="sbClassification">분류</label>
<select id="sbClassification"
hx-get="/sb/signals/list"
hx-include="#sbManufacturer, #sbClassification, #sbAliasName, #sbSearch, #sbGroupCode"
hx-target="#sbList"
hx-params="*"
hx-trigger="change">
<option value="" {% if selected_classification=='' %}selected{% endif %}>전체</option>
{% for cl in signal_classifications %}
<option value="{{ cl }}" {% if cl==selected_classification %}selected{% endif %}>{{ cl }}</option>
{% endfor %}
</select>
</div>
<div style="flex:1; min-width:140px;">
<label for="sbAliasName">차량분류</label>
<select id="sbAliasName" name="alias_name" hx-get="/sb/signals/list" hx-include="#sbManufacturer, #sbClassification, #sbAliasName, #sbSearch, #sbGroupCode" hx-target="#sbList" hx-params="*" hx-trigger="change">
<option value="" {% if selected_alias_name=='' %}selected{% endif %}>전체</option>
{% for an in alias_names %}
<option value="{{ an }}" {% if an==selected_alias_name %}selected{% endif %}>{{ an }}</option>
{% endfor %}
</select>
</div>
{% else %}
<div style="flex: 1; min-width:140px;">
<label for="sbDevice">장치분류</label>
<select id="sbDevice" name="device" hx-get="/sb/faults/list" hx-include="#sbManufacturer, #sbDevice, #sbCarType, #sbAliasName, #sbSearch, #sbGroupCode" hx-target="#sbList" hx-params="*" hx-trigger="change">
<option value="" {% if selected_device=='' %}selected{% endif %}>전체</option>
{% for d in devices|sort %}
<option value="{{ d }}" {% if d==selected_device %}selected{% endif %}>{{ d }}</option>
{% endfor %}
</select>
</div>
<div style="flex: 1; min-width:100px;">
<label for="sbCarType">호차</label>
<select id="sbCarType" name="car_type" hx-get="/sb/faults/list" hx-include="#sbManufacturer, #sbDevice, #sbCarType, #sbAliasName, #sbSearch, #sbGroupCode" hx-target="#sbList" hx-params="*" hx-trigger="change">
<option value="" {% if selected_car_type=='' %}selected{% endif %}>전체</option>
{% for ct in car_types %}
<option value="{{ ct }}" {% if ct==selected_car_type %}selected{% endif %}>{{ ct }}</option>
{% endfor %}
</select>
</div>
<div style="flex:1; min-width:140px;">
<label for="sbAliasName">차량분류</label>
<select id="sbAliasName" name="alias_name" hx-get="/sb/faults/list" hx-include="#sbManufacturer, #sbDevice, #sbCarType, #sbAliasName, #sbSearch, #sbGroupCode" hx-target="#sbList" hx-params="*" hx-trigger="change">
<option value="" {% if selected_alias_name=='' %}selected{% endif %}>전체</option>
{% for an in alias_names %}
<option value="{{ an }}" {% if an==selected_alias_name %}selected{% endif %}>{{ an }}</option>
{% endfor %}
</select>
</div>
{% endif %}
</div>
<div class="search-row" style="display:flex; align-items:center; gap:1rem; margin-bottom:1.2rem;">
<div style="flex:4;">
<label for="sbSearch">검색어</label>
<input id="sbSearch" name="q" value="{{ q }}" placeholder="{% if section=='tcms' %}약어/설명 검색{% else %}코드/고장명 검색{% endif %}"
hx-get="{% if section=='tcms' %}/sb/signals/list{% else %}/sb/faults/list{% endif %}"
hx-trigger="keyup changed delay:200ms, search"
hx-target="#sbList"
hx-include="#sbManufacturer, #sbDevice, #sbCarType, #sbAliasName, #sbClassification, #sbSearch, #sbGroupCode">
</div>
<div style="flex:1; min-width:110px; text-align:right; align-self:end;">
<label for="sbGroupCode" style="margin-bottom:.45em;display:flex;align-items:center;gap:.4em;justify-content:flex-end;">
<input type="checkbox" id="sbGroupCode" name="group_code" value="on"
hx-get="{% if section=='tcms' %}/sb/signals/list{% else %}/sb/faults/list{% endif %}"
hx-target="#sbList"
hx-include="#sbManufacturer, #sbDevice, #sbCarType, #sbAliasName, #sbClassification, #sbSearch, #sbGroupCode"
hx-trigger="change" {% if selected_group_code=='on' %}checked{% endif %}>
코드 묶기
</label>
</div>
</div>
{% if section == 'tcms' %}
<div id="sbList" class="card-list"
hx-get="/sb/signals/list"
hx-trigger="load"
hx-include="#sbManufacturer, #sbClassification, #sbAliasName, #sbSearch, #sbGroupCode">
<!-- Signals 리스트 -->
</div>
{% else %}
<div id="sbList" class="card-list"
hx-get="/sb/faults/list"
hx-trigger="load"
hx-include="#sbManufacturer, #sbDevice, #sbCarType, #sbAliasName, #sbSearch, #sbGroupCode">
<!-- Faults 리스트 -->
</div>
{% endif %}
</section>

View File

@ -0,0 +1,75 @@
<section>
<div class="filter-row" style="display: flex; flex-wrap: wrap; gap: 0.7rem; align-items: end; margin-bottom: 1rem;">
<div style="flex: 1; min-width:160px;">
<label for="mmiManufacturer">제작사</label>
<select id="mmiManufacturer" name="manufacturer"
hx-get="/sb?section=mmicode"
hx-include="#mmiManufacturer,#mmiAliasName,#mmiSearch,#mmiGroupCode"
hx-target="#tabPanel" hx-params="*" hx-trigger="change">
<option value="" {% if selected_manufacturer=='' %}selected{% endif %}>전체</option>
{% for m in manufacturers %}
<option value="{{ m }}" {% if m==selected_manufacturer %}selected{% endif %}>{{ m }}</option>
{% endfor %}
</select>
</div>
<div style="flex:1; min-width:130px;">
<label for="mmiAliasName">차량분류</label>
<select id="mmiAliasName" name="alias_name"
hx-get="/sb?section=mmicode"
hx-include="#mmiManufacturer,#mmiAliasName,#mmiSearch,#mmiGroupCode"
hx-target="#tabPanel" hx-params="*" hx-trigger="change">
<option value="" {% if selected_alias_name=='' %}selected{% endif %}>전체</option>
{% for an in alias_names %}
<option value="{{ an }}" {% if an==selected_alias_name %}selected{% endif %}>{{ an }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="search-row" style="display:flex; align-items:center; gap:1rem; margin-bottom:1.2rem;">
<div style="flex:4;">
<label for="mmiSearch">검색어</label>
<input id="mmiSearch" name="q" value="{{ q }}" placeholder="코드/설명/차량 검색"
hx-get="/sb?section=mmicode"
hx-trigger="keyup changed delay:200ms, search"
hx-target="#tabPanel"
hx-include="#mmiManufacturer,#mmiAliasName,#mmiSearch,#mmiGroupCode">
</div>
<div style="flex:1; min-width:110px; text-align:right; align-self:end;">
<label for="mmiGroupCode" style="margin-bottom:.45em;display:flex;align-items:center;gap:.4em;justify-content:flex-end;">
<input type="checkbox" id="mmiGroupCode" name="group_code" value="on"
hx-get="/sb?section=mmicode"
hx-target="#tabPanel"
hx-include="#mmiManufacturer,#mmiAliasName,#mmiSearch,#mmiGroupCode"
hx-trigger="change" {% if group_code=='on' %}checked{% endif %}>
코드 묶기
</label>
</div>
</div>
<div class="list" {% if page > 0 %} style="display:contents;" {% endif %}>
{% if rows %}
{% for r in rows %}
<article class="item">
<header>
<div class="code">{{ r.code_name }}</div>
<div class="title">{{ r.code_description or '' }}</div>
</header>
<footer>
<small>{{ r.manufacturer or '—' }}{% if r.alias_name %} · {{ r.alias_name }}{% endif %}{% if r.data_type %} · {{ r.data_type }}{% endif %}{% if r.car_id %} · {{ r.car_id }}{% endif %}</small>
</footer>
</article>
{% endfor %}
{% else %}
<p>검색 결과가 없습니다.</p>
{% endif %}
</div>
</section>
{% if rows|length >= page_size %}
<div class="load-more-trigger"
hx-get="/sb?section=mmicode&page={{ page + 1 }}&{{ query_params_string }}"
hx-swap="outerHTML"
hx-trigger="revealed">
<article class="item placeholder">Loading...</article>
</div>
{% endif %}

View File

@ -0,0 +1,24 @@
<article class="modal-card">
<header>
<h3>[{{ row.sig_num }}] {{ row.signal_abbreviation }}</h3>
<button class="outline" _="on click call document.getElementById('modal').close() then put '' into #modal">닫기</button>
</header>
<section class="content">
<p>
<strong>제작사</strong>: {{ row.manufacturer or '—' }}
{% if row.classification %} · <strong>분류</strong>: {{ row.classification }}{% endif %}
{% if row.alias_name %} · <strong>별칭</strong>: {{ row.alias_name }}{% endif %}
</p>
<hr>
<h5>설명</h5>
<p class="prewrap">{{ row.signal_description or '—' }}</p>
<h5>상태값</h5>
<p class="prewrap">{{ row.status_value or '—' }}</p>
<h5>원본 데이터</h5>
<p class="prewrap">{{ row.original_data or '—' }}</p>
<h5>생성/수정</h5>
<p class="prewrap">{{ row.created_at or '—' }} / {{ row.updated_at or '—' }}</p>
</section>
</article>

View File

@ -0,0 +1,28 @@
<div class="list" {% if page > 0 %} style="display:contents;" {% endif %}>
{% if rows %}
{% for r in rows %}
<article class="item" hx-get="/sb/signals/{{ r.id }}" hx-target="#modal" hx-swap="innerHTML show:top">
<header>
<div class="code">{{ r.sig_num }}</div>
<div class="title">{{ r.signal_abbreviation }}</div>
</header>
<footer>
<small>{{ r.manufacturer or '—' }}{% if r.classification %} · {{ r.classification }}{% endif %}{% if r.alias_name %} · {{ r.alias_name }}{% endif %}</small>
</footer>
</article>
{% endfor %}
{% else %}
<p>검색 결과가 없습니다.</p>
{% endif %}
</div>
{% if rows|length >= page_size %}
<div class="load-more-trigger"
hx-get="/sb/signals/list?page={{ page + 1 }}&{{ query_params_string }}"
hx-swap="outerHTML"
hx-trigger="revealed">
<article class="item placeholder">Loading...</article>
</div>
{% endif %}

View File

@ -0,0 +1,2 @@
<div></div>

142
tools/import_fault_codes.py Normal file
View File

@ -0,0 +1,142 @@
import argparse
import os
import sqlite3
from typing import Dict, Any, List, Optional
def find_col(columns: List[str], candidates: List[str]) -> Optional[str]:
lowered = {c.lower(): c for c in columns}
for name in candidates:
if name.lower() in lowered:
return lowered[name.lower()]
return None
def main() -> None:
parser = argparse.ArgumentParser(description="Import fault codes into local data.db from external SQLite table")
parser.add_argument("--src-db", default="fault_codes.db", help="source sqlite file path")
parser.add_argument("--src-table", default="fault_code_list", help="source table name")
parser.add_argument("--manufacturer", default="woojin", help="manufacturer slug (woojin|rotem)")
parser.add_argument("--default-section", default="fault", help="default section if missing (fault|tcms|emergency)")
parser.add_argument("--truncate", action="store_true", help="delete existing faults for manufacturer before import")
args = parser.parse_args()
# destination DB (our app)
try:
from app import DATABASE_PATH # type: ignore
except Exception:
DATABASE_PATH = os.path.join(os.path.dirname(__file__), "..", "data.db")
dest_path = os.path.abspath(DATABASE_PATH)
src_path = os.path.abspath(args.src_db)
if not os.path.exists(src_path):
raise SystemExit(f"Source DB not found: {src_path}")
src = sqlite3.connect(src_path)
src.row_factory = sqlite3.Row
dst = sqlite3.connect(dest_path)
dst.row_factory = sqlite3.Row
dst.execute("PRAGMA foreign_keys = ON")
cur = src.execute(f"SELECT * FROM {args.src_table}")
rows = cur.fetchall()
if not rows:
print("No rows in source table.")
return
columns = rows[0].keys()
code_col = find_col(columns, ["code", "Code", "fault_code", "고장코드", "CODE"]) or "code"
title_col = find_col(columns, ["title", "name", "f.Name", "f_name", "고장명", "fName"]) or None
details_col = find_col(columns, ["details", "detail", "고장상세", "desc", "설명"]) # optional
action_col = find_col(columns, ["action", "조치", "응급조치", "response", "반응"]) # optional
cond_col = find_col(columns, ["condition", "검지조건", "조건"]) # optional
category_col = find_col(columns, ["category", "cat", "장치분류", "system", "type"]) # optional
section_col = find_col(columns, ["section", "섹션", "tab", "kind"]) # optional
print("Column mapping:")
print(" code ->", code_col)
print(" title ->", title_col)
print(" details ->", details_col)
print(" action ->", action_col)
print(" condition ->", cond_col)
print(" category ->", category_col)
print(" section ->", section_col, f"(default={args.default_section})")
# manufacturer ensure
m = dst.execute("SELECT id FROM manufacturer WHERE slug=?", (args.manufacturer.lower(),)).fetchone()
if not m:
name = "우진" if args.manufacturer.lower() == "woojin" else "로템"
dst.execute("INSERT INTO manufacturer (slug, name) VALUES (?, ?)", (args.manufacturer.lower(), name))
m_id = dst.execute("SELECT last_insert_rowid() AS id").fetchone()[0]
else:
m_id = m["id"]
if args.truncate:
dst.execute("DELETE FROM fault WHERE manufacturer_id=?", (m_id,))
dst.execute("DELETE FROM category WHERE manufacturer_id=?", (m_id,))
# existing categories map
cat_map: Dict[str, int] = {}
for r in dst.execute("SELECT id, name FROM category WHERE manufacturer_id=?", (m_id,)):
cat_map[r["name"]] = r["id"]
def ensure_category(name: Optional[str]) -> Optional[int]:
if not name:
return None
name = str(name).strip()
if name == "":
return None
if name in cat_map:
return cat_map[name]
dst.execute("INSERT INTO category (manufacturer_id, name) VALUES (?, ?)", (m_id, name))
cid = dst.execute("SELECT last_insert_rowid() AS id").fetchone()[0]
cat_map[name] = cid
return cid
def get_value(r: sqlite3.Row, key: Optional[str]):
if not key:
return None
for k in r.keys():
if k.lower() == key.lower():
return r[k]
return None
batch: List[tuple] = []
for row in rows:
code = str(get_value(row, code_col) or "").strip()
title = str(get_value(row, title_col) or "").strip()
details = str(get_value(row, details_col) or "").strip()
action = str(get_value(row, action_col) or "").strip()
cond = str(get_value(row, cond_col) or "").strip()
category_val = get_value(row, category_col)
category_val = (str(category_val).strip() if category_val is not None else None)
section_val = get_value(row, section_col)
section = (str(section_val).strip().lower() if section_val is not None else args.default_section)
if section not in ("fault", "tcms", "emergency"):
section = args.default_section
cat_id = ensure_category(category_val)
batch.append((m_id, cat_id, section, code, title, details, action, cond))
dst.executemany(
"INSERT INTO fault (manufacturer_id, category_id, section, code, title, details, action, condition) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
batch,
)
# bump meta version
dst.execute("UPDATE meta SET version = version + 1, updated_at = datetime('now') WHERE id = 1")
if dst.total_changes == 0:
dst.execute("INSERT OR REPLACE INTO meta (id, version, updated_at) VALUES (1, 1, datetime('now'))")
dst.commit()
src.close()
dst.close()
print(f"Imported {len(batch)} rows into {dest_path} for manufacturer '{args.manufacturer.lower()}'.")
if __name__ == "__main__":
main()

50
twa-manifest.json Normal file
View File

@ -0,0 +1,50 @@
{
"packageId": "cc.m1tcloud.tr.twa",
"host": "tr.m1tcloud.cc",
"name": "FaultCode_Line1",
"launcherName": "F_Code",
"display": "standalone",
"themeColor": "#0F172A",
"themeColorDark": "#000000",
"navigationColor": "#000000",
"navigationColorDark": "#000000",
"navigationDividerColor": "#000000",
"navigationDividerColorDark": "#000000",
"backgroundColor": "#0F172A",
"enableNotifications": true,
"startUrl": "/",
"iconUrl": "https://tr.m1tcloud.cc/static/icons/icon-512.png",
"splashScreenFadeOutDuration": 300,
"signingKey": {
"path": "D:\\py\\htmx_test\\android.keystore",
"alias": "android"
},
"appVersionName": "1",
"appVersionCode": 1,
"shortcuts": [],
"generatorApp": "bubblewrap-cli",
"webManifestUrl": "https://tr.m1tcloud.cc/manifest.webmanifest",
"fallbackType": "customtabs",
"features": {
"locationDelegation": {
"enabled": true
}
},
"alphaDependencies": {
"enabled": false
},
"enableSiteSettingsShortcut": true,
"isChromeOSOnly": false,
"isMetaQuest": false,
"fullScopeUrl": "https://tr.m1tcloud.cc/",
"minSdkVersion": 21,
"orientation": "default",
"fingerprints": [],
"additionalTrustedOrigins": [],
"retainedBundles": [],
"protocolHandlers": [],
"fileHandlers": [],
"launchHandlerClientMode": "",
"displayOverride": [],
"appVersion": "1"
}

15007
woo.csv Normal file

File diff suppressed because it is too large Load Diff

15007
woo.csv.bak Normal file

File diff suppressed because it is too large Load Diff

1213
woo_sig.csv Normal file

File diff suppressed because it is too large Load Diff

BIN
woojin.xlsx Normal file

Binary file not shown.

BIN
로템_고장.xlsx Normal file

Binary file not shown.

Binary file not shown.

BIN
우진신호.xlsx Normal file

Binary file not shown.