@ihabdevteam/core 0.134.0 → 0.135.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dist/components.cjs.js +174 -174
  3. package/dist/hooks.cjs.js +1 -1
  4. package/dist/i18n.cjs.js +3 -3
  5. package/dist/ihabdevteam-core.cjs.js +181 -181
  6. package/dist/ihabdevteam-core.d.ts +1 -1
  7. package/dist/packages/core/src/components/BarChart.js +3 -1
  8. package/dist/packages/core/src/components/Popup/ConfirmPopup.js +34 -21
  9. package/dist/packages/core/src/components/Popup/CouponInputPopup.js +4 -1
  10. package/dist/packages/core/src/components/Popup/ListPopup.js +7 -2
  11. package/dist/packages/core/src/components/Popup/SaveTablePopup.js +13 -3
  12. package/dist/packages/core/src/components/Popup/TrainingResultPopup.js +5 -1
  13. package/dist/packages/core/src/components/Report/ReportDocument.js +35 -10
  14. package/dist/packages/core/src/components/RingChart.js +14 -3
  15. package/dist/packages/core/src/components/Table.js +15 -5
  16. package/dist/packages/core/src/components/Training/TrainingBlockButton.js +3 -1
  17. package/dist/packages/core/src/components/Training/TrainingPagePopup.js +28 -2
  18. package/dist/packages/core/src/components/Training/TrainingStepOrderList.js +11 -7
  19. package/dist/packages/core/src/components/Training/TrainingWordCard.js +4 -0
  20. package/dist/packages/core/src/components/Word/WordTableBlock.js +2 -1
  21. package/dist/packages/core/src/components/barChartData.d.ts +8 -0
  22. package/dist/packages/core/src/components/barChartData.js +21 -0
  23. package/dist/packages/core/src/components/useBarChartControl.js +5 -3
  24. package/dist/packages/core/src/hooks/useDailyTrainingSelection.js +17 -1
  25. package/dist/packages/core/src/hooks/useEditableList.d.ts +6 -0
  26. package/dist/packages/core/src/hooks/useEditableList.js +20 -1
  27. package/dist/packages/core/src/hooks/useTablePaging.js +4 -0
  28. package/dist/packages/core/src/hooks/useVisitHistory.js +2 -2
  29. package/dist/packages/core/src/hooks/useVisitSchedule.js +5 -4
  30. package/dist/packages/core/src/hooks/useWordMeanings.js +13 -4
  31. package/dist/packages/core/src/i18n/locales/en.json +2 -1
  32. package/dist/packages/core/src/i18n/locales/ko.json +2 -1
  33. package/dist/packages/core/src/utils/getTrainingData.js +11 -4
  34. package/dist/packages/core/src/utils/imageConfig.js +6 -3
  35. package/dist/packages/core/src/utils/wordTrainingRuntime.d.ts +1 -1
  36. package/dist/packages/core/src/utils/wordTrainingRuntime.js +11 -4
  37. package/dist/packages/core/src/version.d.ts +1 -1
  38. package/dist/packages/core/src/version.js +1 -1
  39. package/dist/utils.cjs.js +1 -1
  40. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { useCallback, useEffect, useRef, useState } from 'react';
3
3
  import { ensureTrainingDataLoaded } from '../utils/getTrainingData';
4
+ import { getTodayKst } from '../utils/date';
4
5
  import { hasItems, resolveSelection, } from '../utils/dailySelectionUtils';
5
6
  // ─── 훅 ──────────────────────────────────────────────────────────────────────
6
7
  /**
@@ -29,6 +30,19 @@ export function useDailyTrainingSelection(userId) {
29
30
  // 가를 수가 없어** 설명 없는 빈 화면을 그리게 된다(실제로 그런 화면이 있다).
30
31
  // loaded 의 뜻을 바꾸면 지금 쓰는 곳이 깨지므로, 가를 수 있는 값을 따로 낸다.
31
32
  const [hasData, setHasData] = useState(false);
33
+ /* 오늘(KST) — 자정을 넘기면 바뀌어 아래 선택을 **새 날짜 시드로** 다시 계산하게 한다. 전에는 userId 만 봐서 화면을 열어 둔 채
34
+ 자정을 넘기면 어제 시드의 세트가 '오늘' 로 보이는데 완료 기록에는 새 날짜가 찍혔다. */
35
+ const [오늘, set오늘] = useState(() => getTodayKst());
36
+ useEffect(() => {
37
+ const 다음자정까지 = () => {
38
+ const 지금 = Date.now();
39
+ const kst = new Date(지금 + 9 * 3600_000);
40
+ const 자정 = Date.UTC(kst.getUTCFullYear(), kst.getUTCMonth(), kst.getUTCDate() + 1) - 9 * 3600_000;
41
+ return Math.max(1000, 자정 - 지금 + 1000);
42
+ };
43
+ let t = setTimeout(function 넘김() { set오늘(getTodayKst()); t = setTimeout(넘김, 다음자정까지()); }, 다음자정까지());
44
+ return () => clearTimeout(t);
45
+ }, []);
32
46
  useEffect(() => {
33
47
  let cancelled = false;
34
48
  let timer;
@@ -90,7 +104,9 @@ export function useDailyTrainingSelection(userId) {
90
104
  tryFill();
91
105
  timer = setInterval(tryFill, 250);
92
106
  return () => { cancelled = true; stop(); };
93
- }, [userId]);
107
+ // 오늘 은 날짜가 바뀌었다는 신호 — resolveSelection 이 안에서 오늘 날짜를 읽는다
108
+ // eslint-disable-next-line react-hooks/exhaustive-deps
109
+ }, [userId, 오늘]);
94
110
  const getTrainingPerType = useCallback((typeKey) => {
95
111
  const src = showYesterday ? yesterdayRef.current : todayRef.current;
96
112
  const fallback = showYesterday ? todayRef.current : yesterdayRef.current;
@@ -3,6 +3,12 @@
3
3
  *
4
4
  * @public
5
5
  */
6
+ /**
7
+ * 같은 항목인가 — 같은 참조이거나, **둘 다 비지 않은 같은 id 를 가지면** 같다(빈 id 는 새로 만든 로컬 항목이라 견주지 않는다).
8
+ * 서버에서 목록을 다시 받으면 항목이 모두 새 객체가 되는데, 고른 항목·지울 항목을 참조로만 견주면 옛 참조가 아무것도
9
+ * 못 찾았다(수정 저장 → 재조회 → 같은 상세에서 '방문' 을 누르면 예정이 안 지워져 방문예정·방문완료 양쪽에 떴다).
10
+ */
11
+ export declare function isSameEditableItem<T>(a: T, b: T): boolean;
6
12
  export declare function useEditableList<T>(emptyItem: T, initialList?: T[]): {
7
13
  list: T[];
8
14
  setList: import("react").Dispatch<import("react").SetStateAction<T[]>>;
@@ -5,6 +5,18 @@ import { useCallback, useEffect, useRef, useState } from 'react';
5
5
  *
6
6
  * @public
7
7
  */
8
+ /**
9
+ * 같은 항목인가 — 같은 참조이거나, **둘 다 비지 않은 같은 id 를 가지면** 같다(빈 id 는 새로 만든 로컬 항목이라 견주지 않는다).
10
+ * 서버에서 목록을 다시 받으면 항목이 모두 새 객체가 되는데, 고른 항목·지울 항목을 참조로만 견주면 옛 참조가 아무것도
11
+ * 못 찾았다(수정 저장 → 재조회 → 같은 상세에서 '방문' 을 누르면 예정이 안 지워져 방문예정·방문완료 양쪽에 떴다).
12
+ */
13
+ export function isSameEditableItem(a, b) {
14
+ if (a === b)
15
+ return true;
16
+ const ia = a?.id;
17
+ const ib = b?.id;
18
+ return ia != null && ia !== '' && ia === ib;
19
+ }
8
20
  export function useEditableList(emptyItem, initialList = []) {
9
21
  const [list, setList] = useState(initialList);
10
22
  const [selected, setSelected] = useState(null);
@@ -42,7 +54,14 @@ export function useEditableList(emptyItem, initialList = []) {
42
54
  return;
43
55
  prevInitialRef.current = initialList;
44
56
  setList(initialList);
57
+ // 고른 항목도 새 목록의 같은 항목(id)으로 옮긴다 — 옛 참조로 남으면 지우기·수정 대상 찾기가 헛돈다
58
+ setSelected(prev => (prev == null ? prev : initialList.find(v => isSameEditableItem(v, prev)) ?? prev));
45
59
  }, [initialList]);
60
+ /* 편집 중이 아닐 때는 편집 폼을 고른 항목에 맞춘다 — 취소하거나(서버 거절로) 되돌려도 폼이 저장 안 된 값을 들고 있어,
61
+ 상세 화면에 거절된 값이 보이고 다시 '수정하기' 를 누르면 그 값에서 시작했다. */
62
+ // eslint-disable-next-line react-hooks/set-state-in-effect
63
+ useEffect(() => { if (!isEditing && selected != null)
64
+ setEditForm({ ...selected }); }, [isEditing, selected]);
46
65
  const openDetail = useCallback((item) => {
47
66
  setSelected(item);
48
67
  setIsEditing(false);
@@ -56,7 +75,7 @@ export function useEditableList(emptyItem, initialList = []) {
56
75
  setIsEditing(false);
57
76
  }, []);
58
77
  const remove = useCallback((item) => {
59
- setList(prev => prev.filter(v => v !== item));
78
+ setList(prev => prev.filter(v => !isSameEditableItem(v, item)));
60
79
  setSelected(null);
61
80
  }, []);
62
81
  return {
@@ -43,6 +43,10 @@ export function useTablePaging(rows, resetOn, opts) {
43
43
  대부분의 경우 빈 표가 된다 */
44
44
  const setPageSize = useCallback((v) => { setPageSizeRaw(v); setPage(1); }, []);
45
45
  const total = Math.max(1, Math.ceil(rows.length / Number(pageSize)));
46
+ /* 행이 줄어 범위를 넘으면 **상태도** 끝 쪽으로 맞춘다 — 보이는 쪽만 조이고 상태를 두면, 필터로 줄었다가 다시 늘 때 예전
47
+ 5쪽으로 튀었다. 렌더 중에 맞춘다(위 resetOn 과 같은 꼴). */
48
+ if (page > total)
49
+ setPage(total);
46
50
  const 이쪽 = Math.min(page, total);
47
51
  const pageRows = useMemo(() => rows.slice((이쪽 - 1) * Number(pageSize), 이쪽 * Number(pageSize)), [rows, 이쪽, pageSize]);
48
52
  return {
@@ -1,5 +1,5 @@
1
1
  import { useState } from 'react';
2
- import { useEditableList } from './useEditableList';
2
+ import { useEditableList, isSameEditableItem } from './useEditableList';
3
3
  /**
4
4
  * @public
5
5
  */
@@ -24,7 +24,7 @@ export function useVisitHistory(initialHistory = []) {
24
24
  closeSubPopup?.();
25
25
  };
26
26
  const handleVisitEditSave = () => {
27
- commitEdit(prev => prev.map(v => v === selectedVisit ? editVisitForm : v), editVisitForm);
27
+ commitEdit(prev => prev.map(v => (selectedVisit != null && isSameEditableItem(v, selectedVisit)) ? editVisitForm : v), editVisitForm);
28
28
  };
29
29
  /** 외부(스케줄 훅 등)에서 방문이력에 항목을 추가할 때 사용 */
30
30
  const addVisit = (item) => {
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { useCallback, useMemo, useState } from 'react';
3
3
  import { today } from './useVisitHistory';
4
- import { useEditableList } from './useEditableList';
4
+ import { useEditableList, isSameEditableItem } from './useEditableList';
5
5
  /**
6
6
  * 차례를 정하는 열쇠 — **가까운 것이 위**.
7
7
  *
@@ -48,17 +48,18 @@ initialSchedule = []) {
48
48
  closeSubPopup?.();
49
49
  };
50
50
  const handleScheduleEditSave = () => {
51
- commitEdit(prev => sortByDate(prev.map(v => v === selectedSchedule ? editScheduleForm : v)), editScheduleForm);
51
+ commitEdit(prev => sortByDate(prev.map(v => (selectedSchedule != null && isSameEditableItem(v, selectedSchedule)) ? editScheduleForm : v)), editScheduleForm);
52
52
  };
53
53
  const handleScheduleDelete = useCallback((item) => {
54
54
  remove(item);
55
55
  }, [remove]);
56
56
  const handleScheduleToVisit = useCallback((item) => {
57
- addVisit({ year: item.year, month: item.month, day: item.day, hour: item.hour, minute: item.minute, memo: item.memo });
57
+ // id 도 넘긴다 — 빠지면 그 방문을 이어서 수정할 때 합성 키로 넘어가 서버가 행을 못 찾았다
58
+ addVisit({ year: item.year, month: item.month, day: item.day, hour: item.hour, minute: item.minute, memo: item.memo, ...(item.id != null ? { id: item.id } : {}) });
58
59
  remove(item);
59
60
  }, [addVisit, remove]);
60
61
  const handleScheduleNoVisit = useCallback((item) => {
61
- setScheduleList(prev => prev.filter(v => v !== item));
62
+ setScheduleList(prev => prev.filter(v => !isSameEditableItem(v, item)));
62
63
  setNoShowList(prev => [item, ...prev]);
63
64
  setSelectedSchedule(null);
64
65
  }, [setScheduleList, setSelectedSchedule]);
@@ -23,26 +23,35 @@ import { useState, useEffect, useMemo } from 'react';
23
23
  export const meaningKey = (word, homonum) => `${word}:${homonum ?? 1}`;
24
24
  // 세션 동안 유지되는 메모리 캐시 — 페이지를 오가도 재조회하지 않는다
25
25
  const memCache = new Map();
26
+ /* 받아 봤는데 뜻이 없던 열쇠 — 다시 묻지 않는다. 전에는 뜻 없는 낱말이 캐시에 안 남아 표를 다시 정렬하거나 쪽을
27
+ 넘길 때마다 같은 낱말을 또 조회했다. (조회가 실패한 것은 여기 넣지 않는다 — 다음에 다시 받는다.) */
28
+ const noMeaning = new Set();
26
29
  export function useWordMeanings(rows, fetchMeanings) {
27
30
  /* 캐시는 모듈 스코프 Map 이라 갱신이 렌더에 자동 반영되지 않는다 — 조회가 끝나면 이 값을
28
31
  올려 아래 스냅샷을 다시 계산시킨다(캐시 히트만으로 리렌더가 연쇄되지 않도록 분리). */
29
32
  const [fetched, setFetched] = useState(0);
30
33
  // rows 는 매 렌더 새 배열이라 의존성으로 쓸 수 없다 — 조회 키만 뽑아 문자열로 비교한다
31
- const wantedKey = rows.map(r => meaningKey(r.word, r.homonum)).join('|');
34
+ // 차례는 조회와 무관하다 — 정렬해 두어야 같은 낱말들을 다시 정렬만 해도 새 조회로 보지 않는다
35
+ const wantedKey = Array.from(new Set(rows.map(r => meaningKey(r.word, r.homonum)))).sort().join('|');
32
36
  useEffect(() => {
33
37
  const wanted = wantedKey ? wantedKey.split('|') : [];
34
- const missing = wanted.filter(k => !memCache.has(k));
38
+ const missing = wanted.filter(k => !memCache.has(k) && !noMeaning.has(k));
35
39
  if (missing.length === 0)
36
40
  return; // 전부 캐시에 있음 — 네트워크·상태 갱신 불필요
37
41
  let cancelled = false;
38
42
  const words = Array.from(new Set(missing.map(k => k.slice(0, k.lastIndexOf(':')))));
39
43
  void fetchMeanings(words).then(got => {
40
- if (cancelled || got.length === 0)
41
- return;
44
+ /* 받은 것은 **취소됐어도 캐시에 담는다** — 쪽을 빨리 넘기면 앞 조회가 취소되며 받은 결과까지 버려, 돌아오면 또
45
+ 조회했다. 화면 갱신(setFetched)만 취소된 쪽에서 건너뛴다. */
42
46
  for (const row of got) {
43
47
  if (row.Meaning)
44
48
  memCache.set(meaningKey(row.Word, row.Homonum ?? undefined), row.Meaning);
45
49
  }
50
+ for (const k of missing)
51
+ if (!memCache.has(k))
52
+ noMeaning.add(k);
53
+ if (cancelled || got.length === 0)
54
+ return;
46
55
  setFetched(v => v + 1);
47
56
  }).catch(() => {
48
57
  // 조회 함수가 약속을 어기고 거절해도(망 끊김 등) 처리되지 않은 거절로 새지 않게 삼킨다 —
@@ -742,7 +742,8 @@
742
742
  "descriptionPlaceholder": "Enter a description for this word table.",
743
743
  "duplicateName": "This name is already in use.",
744
744
  "nameRequired": "Please enter a table name.",
745
- "autoFillHint": " (Press Tab or Space to autofill)"
745
+ "autoFillHint": " (Press Tab or Space to autofill)",
746
+ "copyName": "{{name}} (copy)"
746
747
  },
747
748
  "loadTable": {
748
749
  "title": "Load Saved Word Table",
@@ -740,7 +740,8 @@
740
740
  "descriptionPlaceholder": "이 단어표에 대한 설명을 입력하세요.",
741
741
  "duplicateName": "이미 사용 중인 이름입니다.",
742
742
  "nameRequired": "표 이름을 입력해 주세요.",
743
- "autoFillHint": " (Tab 또는 space 입력시 자동 입력)"
743
+ "autoFillHint": " (Tab 또는 space 입력시 자동 입력)",
744
+ "copyName": "{{name}} (복사)"
744
745
  },
745
746
  "loadTable": {
746
747
  "title": "저장된 단어 표 불러오기",
@@ -104,6 +104,9 @@ export async function loadTrainingData() {
104
104
  catch (e) { }
105
105
  return;
106
106
  }
107
+ /* 다 읽은 뒤에 한꺼번에 넣는다 — 도중에 형식 오류로 멈추면 이미 넣은 것이 남은 채 재시도가 또 넣어, 부를 때마다 풀에
108
+ 중복이 쌓였다(하루 선택이 중복 문항을 뽑고 기기마다 세트가 달라짐). */
109
+ const 받음 = { noise: [], short: [], long: [], order: [] };
107
110
  try {
108
111
  for (const key of Object.keys(map || {})) {
109
112
  const groups = map[key] || [];
@@ -115,16 +118,16 @@ export async function loadTrainingData() {
115
118
  // Preserve original group structure from allTrainingData.json
116
119
  const groupObj = Object.assign({}, gr, { training_type: t, category_0: cat0, category_1: cat1 });
117
120
  if (t === 'LISTEN_IN_NOISE') {
118
- NOISE_DATAS.push(groupObj);
121
+ 받음.noise.push(groupObj);
119
122
  }
120
123
  else if (t === 'SHORT_STORY') {
121
- SHORT_STORIES.push(groupObj);
124
+ 받음.short.push(groupObj);
122
125
  }
123
126
  else if (t === 'LONG_STORY') {
124
- LONG_STORIES.push(groupObj);
127
+ 받음.long.push(groupObj);
125
128
  }
126
129
  else if (t === 'ORDERING_SENTENCE') {
127
- ORDER_SETS.push(groupObj);
130
+ 받음.order.push(groupObj);
128
131
  }
129
132
  }
130
133
  }
@@ -142,6 +145,10 @@ export async function loadTrainingData() {
142
145
  catch { /* SSR 등 */ }
143
146
  return;
144
147
  }
148
+ NOISE_DATAS.push(...받음.noise);
149
+ SHORT_STORIES.push(...받음.short);
150
+ LONG_STORIES.push(...받음.long);
151
+ ORDER_SETS.push(...받음.order);
145
152
  try {
146
153
  window.dispatchEvent(new Event('speechear.data.updated'));
147
154
  }
@@ -60,13 +60,16 @@ export async function checkImageExists(word, homonum) {
60
60
  return false;
61
61
  // 타임아웃 없으면 HEAD 요청 하나가 응답 없이 걸릴 때 이 확인을 기다리는
62
62
  // 검색 버튼(이미지 있음 필터)이 영구 비활성화된다
63
+ /* **확인이 안 된 것은 캐시하지 않는다.** 망 끊김·타임아웃·5xx 까지 '이미지 없음' 으로 영구 저장해, 한 번 끊긴 단어는 망이
64
+ 돌아와도 '이미지 있음' 필터에서 계속 빠졌다(ttsConfig.checkAudioExists 와 같게). 있음(2xx)·없음(4xx)만 담는다. */
63
65
  const p = fetch(url, { method: 'HEAD', signal: AbortSignal.timeout(8000) })
64
- .then(res => res.ok)
65
- .catch(() => false)
66
+ .then(res => (res.status >= 500 ? null : res.ok), () => null)
66
67
  .then(ok => {
68
+ inflight.delete(k);
69
+ if (ok === null)
70
+ return false;
67
71
  map[k] = ok;
68
72
  scheduleSave();
69
- inflight.delete(k);
70
73
  return ok;
71
74
  });
72
75
  inflight.set(k, p);
@@ -9,7 +9,7 @@ export declare function generateRuntimeItems(typeKey: WordTrainingTypeKey, opts?
9
9
  * 공통 조합을 백그라운드에서 사전 계산해 결과 캐시를 미리 채운다.
10
10
  * 이후 동일 조합으로 generateRuntimeItems 호출 시 즉시(<5ms) 반환.
11
11
  * fire-and-forget — 완료 여부에 관계없이 UI 블로킹 없음.
12
- * UI가 실제로 쓰는 difficulty(cap.difficulty→'count', 그 외 null)만 예열한다.
12
+ * UI가 실제로 쓰는 열쇠(count 5 · 난이도 'random'/'count' · 필터 기본값)로 예열한다.
13
13
  * (과거 대립종류 cho/jung/jong 예열은 UI 미사용이고 정작 count는 누락돼 첫 생성이 느렸음)
14
14
  */
15
15
  export declare function precomputeCommonCombinations(excludeNoImage: boolean): Promise<void>;
@@ -599,11 +599,14 @@ async function _generateRuntimeItems(typeKey, opts = {}) {
599
599
  * 공통 조합을 백그라운드에서 사전 계산해 결과 캐시를 미리 채운다.
600
600
  * 이후 동일 조합으로 generateRuntimeItems 호출 시 즉시(<5ms) 반환.
601
601
  * fire-and-forget — 완료 여부에 관계없이 UI 블로킹 없음.
602
- * UI가 실제로 쓰는 difficulty(cap.difficulty→'count', 그 외 null)만 예열한다.
602
+ * UI가 실제로 쓰는 열쇠(count 5 · 난이도 'random'/'count' · 필터 기본값)로 예열한다.
603
603
  * (과거 대립종류 cho/jung/jong 예열은 UI 미사용이고 정작 count는 누락돼 첫 생성이 느렸음)
604
604
  */
605
605
  export async function precomputeCommonCombinations(excludeNoImage) {
606
- const difficulties = [null, 'count'];
606
+ /* **화면이 실제로 부르는 열쇠와 똑같이** 예열한다. 전에는 count:10 · 난이도 null/'count' 로 돌았는데 화면
607
+ (useWordTrainingDataGeneration)은 count:5 · 필터 기본 난이도 'random'(음소를 고르면 'count')으로 불러 캐시 열쇠가
608
+ 달랐다 — 예열 계산을 전부 버리고 첫 생성이 다시 계산했다. */
609
+ const difficulties = ['random', 'count'];
607
610
  const types = Object.keys(CAPABILITY);
608
611
  let tick = 0;
609
612
  for (const diff of difficulties) {
@@ -611,9 +614,13 @@ export async function precomputeCommonCombinations(excludeNoImage) {
611
614
  const cap = CAPABILITY[t];
612
615
  generateRuntimeItems(t, {
613
616
  difficulty: cap.difficulty ? diff : null,
614
- choseong: null,
615
- count: 10,
617
+ contrastCount: 1,
618
+ composition: null,
619
+ count: 5,
616
620
  excludeNoImage,
621
+ sameStructureOnly: true,
622
+ phonemeView: 'char',
623
+ grade: null,
617
624
  }).catch(() => { });
618
625
  // 6 타입마다 마이크로태스크 양보 → 메인 스레드 프레임 블로킹 방지
619
626
  if (++tick % 6 === 0)
@@ -46,4 +46,4 @@
46
46
  *
47
47
  * 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
48
48
  */
49
- export declare const CORE_VERSION = "0.134.0";
49
+ export declare const CORE_VERSION = "0.135.0";
@@ -46,4 +46,4 @@
46
46
  *
47
47
  * 값은 손으로 적고 `test/coreVersion.test.ts` 가 package.json 과 어긋나면 운다.
48
48
  */
49
- export const CORE_VERSION = '0.134.0';
49
+ export const CORE_VERSION = '0.135.0';