spoclip-kit 2.5.0 → 2.6.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.
package/dist/index.cjs CHANGED
@@ -24,6 +24,7 @@ __export(src_exports, {
24
24
  MIN_DOWNLOAD_SECONDS: () => MIN_DOWNLOAD_SECONDS,
25
25
  ORIGINAL_DOWNLOAD_COST_POLICY: () => ORIGINAL_DOWNLOAD_COST_POLICY,
26
26
  calculateGymPartnerRegistrationGold: () => calculateGymPartnerRegistrationGold,
27
+ calculateGymPartnerRegistrationGoldTotal: () => calculateGymPartnerRegistrationGoldTotal,
27
28
  calculateOriginalDownloadCostWon: () => calculateOriginalDownloadCostWon,
28
29
  color: () => color,
29
30
  colorV2: () => colorV2,
@@ -97,6 +98,20 @@ function calculateGymPartnerRegistrationGold(input) {
97
98
  goldAmount: days * GYM_PARTNER_REGISTRATION_GOLD_POLICY.goldPerDay
98
99
  };
99
100
  }
101
+ function calculateGymPartnerRegistrationGoldTotal(periods) {
102
+ const items = periods.map((period) => {
103
+ const result = calculateGymPartnerRegistrationGold(period);
104
+ return {
105
+ ...period,
106
+ ...result
107
+ };
108
+ });
109
+ return {
110
+ totalDays: items.reduce((sum, item) => sum + item.days, 0),
111
+ totalGoldAmount: items.reduce((sum, item) => sum + item.goldAmount, 0),
112
+ items
113
+ };
114
+ }
100
115
  function parseKstDateString(input, fieldName) {
101
116
  const dateOnlyMatch = DATE_ONLY_PATTERN.exec(input);
102
117
  if (dateOnlyMatch === null) {
@@ -364,6 +379,7 @@ var mobileTypo = {
364
379
  MIN_DOWNLOAD_SECONDS,
365
380
  ORIGINAL_DOWNLOAD_COST_POLICY,
366
381
  calculateGymPartnerRegistrationGold,
382
+ calculateGymPartnerRegistrationGoldTotal,
367
383
  calculateOriginalDownloadCostWon,
368
384
  color,
369
385
  colorV2,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/libs/cost.ts","../src/libs/gym-registration-gold.ts","../src/styles/color.ts","../src/styles/typo.ts"],"sourcesContent":["// Main entry point for spoclip-kit\nexport * from './libs';\nexport * from './types';\nexport * from './styles';\n","import type { TicketCode } from '../types/membership';\n\nexport const ORIGINAL_DOWNLOAD_COST_POLICY = {\n baseDurationSeconds: 600,\n baseCostWon: 2000,\n fullVideoDiscountRate: 0.1,\n /**\n * v14 신정책: 멤버십 plan별 분당 KRW 요율.\n * `calculateOriginalDownloadCostWon(duration, { plan })`로 호출 시 적용된다.\n * plan 미지정 시 `baseCostWon/baseDurationSeconds` 기반 단일 단가(레거시 200원/분)로 폴백 — 기존 호출자 호환.\n */\n perPlanKrwPerMinute: {\n FREE: 100,\n PLUS: 50,\n PRO: 25,\n },\n /**\n * 멤버십 plan별 무료 다운로드 구간(초).\n * 다운로드 길이 중 앞 `freeSecondsByPlan[plan]`초는 과금에서 제외하고,\n * 이를 초과한 구간에만 `perPlanKrwPerMinute[plan]` 요율을 적용한다 (초과분 과금).\n * plan 미지정(레거시 단일 단가) 경로에는 무료 구간을 적용하지 않는다.\n */\n freeSecondsByPlan: {\n FREE: 10,\n PLUS: 20,\n PRO: 30,\n },\n} as const;\n\n/**\n * 다운로드 가능한 최소 길이(초).\n * 이보다 짧은 구간은 다운로드 요청 자체를 허용하지 않는다 (server·FE 공용 제약).\n */\nexport const MIN_DOWNLOAD_SECONDS = 10;\n\n/**\n * 다운로드 요청 길이가 허용되는지 검사한다.\n * `durationSeconds >= MIN_DOWNLOAD_SECONDS`일 때만 true.\n */\nexport function isDownloadDurationAllowed(durationSeconds: number): boolean {\n return (\n Number.isFinite(durationSeconds) && durationSeconds >= MIN_DOWNLOAD_SECONDS\n );\n}\n\nexport interface OriginalDownloadCostOptions {\n isFullVideo?: boolean;\n /**\n * 멤버십 plan. 지정 시 `perPlanKrwPerMinute[plan]`을 단가로,\n * `freeSecondsByPlan[plan]`을 무료 구간으로 적용한다.\n * 미지정(undefined) 시 레거시 단일 단가(`baseCostWon/baseDurationSeconds`) + 무료 구간 없음.\n */\n plan?: TicketCode;\n}\n\nexport function calculateOriginalDownloadCostWon(\n durationSeconds: number,\n options: OriginalDownloadCostOptions = {},\n): number {\n if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) return 0;\n\n const { plan, isFullVideo } = options;\n\n // plan 지정 시 앞 freeSeconds 구간은 과금에서 제외하고 초과분만 과금한다.\n // 레거시 경로(plan 미지정)는 무료 구간 0으로 기존 동작을 유지한다.\n const freeSeconds =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.freeSecondsByPlan[plan]\n : 0;\n const billableSeconds = Math.max(0, durationSeconds - freeSeconds);\n if (billableSeconds <= 0) return 0;\n\n const unitPricePerSecond =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.perPlanKrwPerMinute[plan] / 60\n : ORIGINAL_DOWNLOAD_COST_POLICY.baseCostWon /\n ORIGINAL_DOWNLOAD_COST_POLICY.baseDurationSeconds;\n\n const discountMultiplier = isFullVideo\n ? 1 - ORIGINAL_DOWNLOAD_COST_POLICY.fullVideoDiscountRate\n : 1;\n\n // 부동소수점 drift(예: 30.000000000000004) 때문에 1원 과다 청구되는 것을 막기 위해\n // 원 단위 미만 노이즈를 보정한 뒤 올림한다.\n const rawCost = billableSeconds * unitPricePerSecond * discountMultiplier;\n return Math.ceil(Number(rawCost.toFixed(6)));\n}\n","export const GYM_PARTNER_REGISTRATION_GOLD_POLICY = {\n goldPerDay: 350,\n timeZone: 'Asia/Seoul',\n} as const;\n\nexport interface GymPartnerRegistrationGoldInput {\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n startDate: string;\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n endDate: string;\n}\n\nexport interface GymPartnerRegistrationGoldResult {\n days: number;\n goldAmount: number;\n}\n\ninterface KstDateParts {\n year: number;\n month: number;\n day: number;\n}\n\nconst DATE_ONLY_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\n\nexport function calculateGymPartnerRegistrationGold(\n input: GymPartnerRegistrationGoldInput,\n): GymPartnerRegistrationGoldResult {\n const startDate = parseKstDateString(input.startDate, 'startDate');\n const endDate = parseKstDateString(input.endDate, 'endDate');\n\n const startDateKey = toUtcDateKey(startDate);\n const endDateKey = toUtcDateKey(endDate);\n\n if (endDateKey < startDateKey) {\n throw new RangeError('endDate must be on or after startDate');\n }\n\n const days = Math.floor((endDateKey - startDateKey) / MS_PER_DAY) + 1;\n\n return {\n days,\n goldAmount: days * GYM_PARTNER_REGISTRATION_GOLD_POLICY.goldPerDay,\n };\n}\n\nfunction parseKstDateString(\n input: string,\n fieldName: 'startDate' | 'endDate',\n): KstDateParts {\n const dateOnlyMatch = DATE_ONLY_PATTERN.exec(input);\n if (dateOnlyMatch === null) {\n throw new RangeError(`${fieldName} must be a YYYY-MM-DD date`);\n }\n\n const year = Number(dateOnlyMatch[1]);\n const month = Number(dateOnlyMatch[2]);\n const day = Number(dateOnlyMatch[3]);\n const utcDate = new Date(Date.UTC(year, month - 1, day));\n\n if (\n utcDate.getUTCFullYear() !== year ||\n utcDate.getUTCMonth() + 1 !== month ||\n utcDate.getUTCDate() !== day\n ) {\n throw new RangeError(`${fieldName} must be a valid date`);\n }\n\n return { year, month, day };\n}\n\nfunction toUtcDateKey(date: KstDateParts): number {\n return Date.UTC(date.year, date.month - 1, date.day);\n}\n","const color = {\n primary: '#242535',\n primaryB: '#515265',\n primary2: '#9335FB',\n primary2B: '#6323AA',\n secondary: '#FADD32',\n bgColor: '#F3F4F6',\n error: '#EA2E2E',\n\n white: '#FFFFFF',\n black: '#111111',\n gray1: '#424242',\n gray2: '#616161',\n gray3: '#8E8E8E',\n gray4: '#BDBDBD',\n gray5: '#E0E0E0',\n gray6: '#EDEDED',\n\n sub: '#A3A4BD',\n sub3: '#5561E8',\n sub3b: '#F1F7FF',\n sub4: '#1D1A1A',\n\n point: '#FADD32',\n} as const;\n\n/**\n * @deprecated use color instead (작명 실수 ㅠ)\n */\n\nconst colorV2 = color;\n\nexport { colorV2, color };\n","type TypographyKey =\n | 'h1-24b'\n | 'h1-24m'\n | 'h1-24'\n | 'h2-20b'\n | 'h2-20m'\n | 'h2-20'\n | 'h3-18b'\n | 'h3-18m'\n | 'h3-18'\n | 'b1-16b'\n | 'b1-16m'\n | 'b1-16'\n | 'b2-14b'\n | 'b2-14m'\n | 'b2-14'\n | 'b3-12b'\n | 'b3-12m'\n | 'b3-12'\n | 's1-10b'\n | 's1-10m'\n | 's1-10';\n\nconst typo = {\n 'h1-24b': {\n fontSize: 24,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h1-24m': {\n fontSize: 24,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h1-24': {\n fontSize: 24,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 'h2-20b': {\n fontSize: 20,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h2-20m': {\n fontSize: 20,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h2-20': {\n fontSize: 20,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h3-18b': {\n fontSize: 18,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h3-18m': {\n fontSize: 18,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h3-18': {\n fontSize: 18,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b1-16b': {\n fontSize: 16,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b1-16m': {\n fontSize: 16,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b1-16': {\n fontSize: 16,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 'b2-14b': {\n fontSize: 14,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b2-14m': {\n fontSize: 14,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b2-14': {\n fontSize: 14,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 'b3-12b': {\n fontSize: 12,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b3-12m': {\n fontSize: 12,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b3-12': {\n fontSize: 12,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 's1-10b': {\n fontSize: 10,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 's1-10m': {\n fontSize: 10,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 's1-10': {\n fontSize: 10,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n} as const;\n\nconst transformLineheight = (key: TypographyKey) => {\n return typo[key].lineHeight * typo[key].fontSize;\n};\n\nconst mobileTypo = {\n 'h1-24b': {\n ...typo['h1-24b'],\n lineHeight: transformLineheight('h1-24b'),\n },\n\n 'h1-24m': {\n ...typo['h1-24m'],\n lineHeight: transformLineheight('h1-24m'),\n },\n\n 'h1-24': {\n ...typo['h1-24'],\n lineHeight: transformLineheight('h1-24'),\n },\n\n 'h2-20b': {\n ...typo['h2-20b'],\n lineHeight: transformLineheight('h2-20b'),\n },\n\n 'h2-20m': {\n ...typo['h2-20m'],\n lineHeight: transformLineheight('h2-20m'),\n },\n\n 'h2-20': {\n ...typo['h2-20'],\n lineHeight: transformLineheight('h2-20'),\n },\n\n 'h3-18b': {\n ...typo['h3-18b'],\n lineHeight: transformLineheight('h3-18b'),\n },\n\n 'h3-18m': {\n ...typo['h3-18m'],\n lineHeight: transformLineheight('h3-18m'),\n },\n\n 'h3-18': {\n ...typo['h3-18'],\n lineHeight: transformLineheight('h3-18'),\n },\n\n 'b1-16b': {\n ...typo['b1-16b'],\n lineHeight: transformLineheight('b1-16b'),\n },\n\n 'b1-16m': {\n ...typo['b1-16m'],\n lineHeight: transformLineheight('b1-16m'),\n },\n\n 'b1-16': {\n ...typo['b1-16'],\n lineHeight: transformLineheight('b1-16'),\n },\n\n 'b2-14b': {\n ...typo['b2-14b'],\n lineHeight: transformLineheight('b2-14b'),\n },\n\n 'b2-14m': {\n ...typo['b2-14m'],\n lineHeight: transformLineheight('b2-14m'),\n },\n\n 'b2-14': {\n ...typo['b2-14'],\n lineHeight: transformLineheight('b2-14'),\n },\n\n 'b3-12b': {\n ...typo['b3-12b'],\n lineHeight: transformLineheight('b3-12b'),\n },\n\n 'b3-12m': {\n ...typo['b3-12m'],\n lineHeight: transformLineheight('b3-12m'),\n },\n\n 'b3-12': {\n ...typo['b3-12'],\n lineHeight: transformLineheight('b3-12'),\n },\n\n 's1-10b': {\n ...typo['s1-10b'],\n lineHeight: transformLineheight('s1-10b'),\n },\n\n 's1-10m': {\n ...typo['s1-10m'],\n lineHeight: transformLineheight('s1-10m'),\n },\n\n 's1-10': {\n ...typo['s1-10'],\n lineHeight: transformLineheight('s1-10'),\n },\n} as const;\n\nexport { typo, mobileTypo };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/libs/cost.ts","../src/libs/gym-registration-gold.ts","../src/styles/color.ts","../src/styles/typo.ts"],"sourcesContent":["// Main entry point for spoclip-kit\nexport * from './libs';\nexport * from './types';\nexport * from './styles';\n","import type { TicketCode } from '../types/membership';\n\nexport const ORIGINAL_DOWNLOAD_COST_POLICY = {\n baseDurationSeconds: 600,\n baseCostWon: 2000,\n fullVideoDiscountRate: 0.1,\n /**\n * v14 신정책: 멤버십 plan별 분당 KRW 요율.\n * `calculateOriginalDownloadCostWon(duration, { plan })`로 호출 시 적용된다.\n * plan 미지정 시 `baseCostWon/baseDurationSeconds` 기반 단일 단가(레거시 200원/분)로 폴백 — 기존 호출자 호환.\n */\n perPlanKrwPerMinute: {\n FREE: 100,\n PLUS: 50,\n PRO: 25,\n },\n /**\n * 멤버십 plan별 무료 다운로드 구간(초).\n * 다운로드 길이 중 앞 `freeSecondsByPlan[plan]`초는 과금에서 제외하고,\n * 이를 초과한 구간에만 `perPlanKrwPerMinute[plan]` 요율을 적용한다 (초과분 과금).\n * plan 미지정(레거시 단일 단가) 경로에는 무료 구간을 적용하지 않는다.\n */\n freeSecondsByPlan: {\n FREE: 10,\n PLUS: 20,\n PRO: 30,\n },\n} as const;\n\n/**\n * 다운로드 가능한 최소 길이(초).\n * 이보다 짧은 구간은 다운로드 요청 자체를 허용하지 않는다 (server·FE 공용 제약).\n */\nexport const MIN_DOWNLOAD_SECONDS = 10;\n\n/**\n * 다운로드 요청 길이가 허용되는지 검사한다.\n * `durationSeconds >= MIN_DOWNLOAD_SECONDS`일 때만 true.\n */\nexport function isDownloadDurationAllowed(durationSeconds: number): boolean {\n return (\n Number.isFinite(durationSeconds) && durationSeconds >= MIN_DOWNLOAD_SECONDS\n );\n}\n\nexport interface OriginalDownloadCostOptions {\n isFullVideo?: boolean;\n /**\n * 멤버십 plan. 지정 시 `perPlanKrwPerMinute[plan]`을 단가로,\n * `freeSecondsByPlan[plan]`을 무료 구간으로 적용한다.\n * 미지정(undefined) 시 레거시 단일 단가(`baseCostWon/baseDurationSeconds`) + 무료 구간 없음.\n */\n plan?: TicketCode;\n}\n\nexport function calculateOriginalDownloadCostWon(\n durationSeconds: number,\n options: OriginalDownloadCostOptions = {},\n): number {\n if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) return 0;\n\n const { plan, isFullVideo } = options;\n\n // plan 지정 시 앞 freeSeconds 구간은 과금에서 제외하고 초과분만 과금한다.\n // 레거시 경로(plan 미지정)는 무료 구간 0으로 기존 동작을 유지한다.\n const freeSeconds =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.freeSecondsByPlan[plan]\n : 0;\n const billableSeconds = Math.max(0, durationSeconds - freeSeconds);\n if (billableSeconds <= 0) return 0;\n\n const unitPricePerSecond =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.perPlanKrwPerMinute[plan] / 60\n : ORIGINAL_DOWNLOAD_COST_POLICY.baseCostWon /\n ORIGINAL_DOWNLOAD_COST_POLICY.baseDurationSeconds;\n\n const discountMultiplier = isFullVideo\n ? 1 - ORIGINAL_DOWNLOAD_COST_POLICY.fullVideoDiscountRate\n : 1;\n\n // 부동소수점 drift(예: 30.000000000000004) 때문에 1원 과다 청구되는 것을 막기 위해\n // 원 단위 미만 노이즈를 보정한 뒤 올림한다.\n const rawCost = billableSeconds * unitPricePerSecond * discountMultiplier;\n return Math.ceil(Number(rawCost.toFixed(6)));\n}\n","export const GYM_PARTNER_REGISTRATION_GOLD_POLICY = {\n goldPerDay: 350,\n timeZone: 'Asia/Seoul',\n} as const;\n\nexport interface GymPartnerRegistrationGoldInput {\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n startDate: string;\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n endDate: string;\n}\n\nexport interface GymPartnerRegistrationGoldResult {\n days: number;\n goldAmount: number;\n}\n\nexport interface GymPartnerRegistrationGoldTotalItem\n extends GymPartnerRegistrationGoldInput,\n GymPartnerRegistrationGoldResult {}\n\nexport interface GymPartnerRegistrationGoldTotalResult {\n totalDays: number;\n totalGoldAmount: number;\n items: GymPartnerRegistrationGoldTotalItem[];\n}\n\ninterface KstDateParts {\n year: number;\n month: number;\n day: number;\n}\n\nconst DATE_ONLY_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\n\nexport function calculateGymPartnerRegistrationGold(\n input: GymPartnerRegistrationGoldInput,\n): GymPartnerRegistrationGoldResult {\n const startDate = parseKstDateString(input.startDate, 'startDate');\n const endDate = parseKstDateString(input.endDate, 'endDate');\n\n const startDateKey = toUtcDateKey(startDate);\n const endDateKey = toUtcDateKey(endDate);\n\n if (endDateKey < startDateKey) {\n throw new RangeError('endDate must be on or after startDate');\n }\n\n const days = Math.floor((endDateKey - startDateKey) / MS_PER_DAY) + 1;\n\n return {\n days,\n goldAmount: days * GYM_PARTNER_REGISTRATION_GOLD_POLICY.goldPerDay,\n };\n}\n\nexport function calculateGymPartnerRegistrationGoldTotal(\n periods: GymPartnerRegistrationGoldInput[],\n): GymPartnerRegistrationGoldTotalResult {\n const items = periods.map((period) => {\n const result = calculateGymPartnerRegistrationGold(period);\n\n return {\n ...period,\n ...result,\n };\n });\n\n return {\n totalDays: items.reduce((sum, item) => sum + item.days, 0),\n totalGoldAmount: items.reduce((sum, item) => sum + item.goldAmount, 0),\n items,\n };\n}\n\nfunction parseKstDateString(\n input: string,\n fieldName: 'startDate' | 'endDate',\n): KstDateParts {\n const dateOnlyMatch = DATE_ONLY_PATTERN.exec(input);\n if (dateOnlyMatch === null) {\n throw new RangeError(`${fieldName} must be a YYYY-MM-DD date`);\n }\n\n const year = Number(dateOnlyMatch[1]);\n const month = Number(dateOnlyMatch[2]);\n const day = Number(dateOnlyMatch[3]);\n const utcDate = new Date(Date.UTC(year, month - 1, day));\n\n if (\n utcDate.getUTCFullYear() !== year ||\n utcDate.getUTCMonth() + 1 !== month ||\n utcDate.getUTCDate() !== day\n ) {\n throw new RangeError(`${fieldName} must be a valid date`);\n }\n\n return { year, month, day };\n}\n\nfunction toUtcDateKey(date: KstDateParts): number {\n return Date.UTC(date.year, date.month - 1, date.day);\n}\n","const color = {\n primary: '#242535',\n primaryB: '#515265',\n primary2: '#9335FB',\n primary2B: '#6323AA',\n secondary: '#FADD32',\n bgColor: '#F3F4F6',\n error: '#EA2E2E',\n\n white: '#FFFFFF',\n black: '#111111',\n gray1: '#424242',\n gray2: '#616161',\n gray3: '#8E8E8E',\n gray4: '#BDBDBD',\n gray5: '#E0E0E0',\n gray6: '#EDEDED',\n\n sub: '#A3A4BD',\n sub3: '#5561E8',\n sub3b: '#F1F7FF',\n sub4: '#1D1A1A',\n\n point: '#FADD32',\n} as const;\n\n/**\n * @deprecated use color instead (작명 실수 ㅠ)\n */\n\nconst colorV2 = color;\n\nexport { colorV2, color };\n","type TypographyKey =\n | 'h1-24b'\n | 'h1-24m'\n | 'h1-24'\n | 'h2-20b'\n | 'h2-20m'\n | 'h2-20'\n | 'h3-18b'\n | 'h3-18m'\n | 'h3-18'\n | 'b1-16b'\n | 'b1-16m'\n | 'b1-16'\n | 'b2-14b'\n | 'b2-14m'\n | 'b2-14'\n | 'b3-12b'\n | 'b3-12m'\n | 'b3-12'\n | 's1-10b'\n | 's1-10m'\n | 's1-10';\n\nconst typo = {\n 'h1-24b': {\n fontSize: 24,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h1-24m': {\n fontSize: 24,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h1-24': {\n fontSize: 24,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 'h2-20b': {\n fontSize: 20,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h2-20m': {\n fontSize: 20,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h2-20': {\n fontSize: 20,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h3-18b': {\n fontSize: 18,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h3-18m': {\n fontSize: 18,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h3-18': {\n fontSize: 18,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b1-16b': {\n fontSize: 16,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b1-16m': {\n fontSize: 16,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b1-16': {\n fontSize: 16,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 'b2-14b': {\n fontSize: 14,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b2-14m': {\n fontSize: 14,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b2-14': {\n fontSize: 14,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 'b3-12b': {\n fontSize: 12,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b3-12m': {\n fontSize: 12,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b3-12': {\n fontSize: 12,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 's1-10b': {\n fontSize: 10,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 's1-10m': {\n fontSize: 10,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 's1-10': {\n fontSize: 10,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n} as const;\n\nconst transformLineheight = (key: TypographyKey) => {\n return typo[key].lineHeight * typo[key].fontSize;\n};\n\nconst mobileTypo = {\n 'h1-24b': {\n ...typo['h1-24b'],\n lineHeight: transformLineheight('h1-24b'),\n },\n\n 'h1-24m': {\n ...typo['h1-24m'],\n lineHeight: transformLineheight('h1-24m'),\n },\n\n 'h1-24': {\n ...typo['h1-24'],\n lineHeight: transformLineheight('h1-24'),\n },\n\n 'h2-20b': {\n ...typo['h2-20b'],\n lineHeight: transformLineheight('h2-20b'),\n },\n\n 'h2-20m': {\n ...typo['h2-20m'],\n lineHeight: transformLineheight('h2-20m'),\n },\n\n 'h2-20': {\n ...typo['h2-20'],\n lineHeight: transformLineheight('h2-20'),\n },\n\n 'h3-18b': {\n ...typo['h3-18b'],\n lineHeight: transformLineheight('h3-18b'),\n },\n\n 'h3-18m': {\n ...typo['h3-18m'],\n lineHeight: transformLineheight('h3-18m'),\n },\n\n 'h3-18': {\n ...typo['h3-18'],\n lineHeight: transformLineheight('h3-18'),\n },\n\n 'b1-16b': {\n ...typo['b1-16b'],\n lineHeight: transformLineheight('b1-16b'),\n },\n\n 'b1-16m': {\n ...typo['b1-16m'],\n lineHeight: transformLineheight('b1-16m'),\n },\n\n 'b1-16': {\n ...typo['b1-16'],\n lineHeight: transformLineheight('b1-16'),\n },\n\n 'b2-14b': {\n ...typo['b2-14b'],\n lineHeight: transformLineheight('b2-14b'),\n },\n\n 'b2-14m': {\n ...typo['b2-14m'],\n lineHeight: transformLineheight('b2-14m'),\n },\n\n 'b2-14': {\n ...typo['b2-14'],\n lineHeight: transformLineheight('b2-14'),\n },\n\n 'b3-12b': {\n ...typo['b3-12b'],\n lineHeight: transformLineheight('b3-12b'),\n },\n\n 'b3-12m': {\n ...typo['b3-12m'],\n lineHeight: transformLineheight('b3-12m'),\n },\n\n 'b3-12': {\n ...typo['b3-12'],\n lineHeight: transformLineheight('b3-12'),\n },\n\n 's1-10b': {\n ...typo['s1-10b'],\n lineHeight: transformLineheight('s1-10b'),\n },\n\n 's1-10m': {\n ...typo['s1-10m'],\n lineHeight: transformLineheight('s1-10m'),\n },\n\n 's1-10': {\n ...typo['s1-10'],\n lineHeight: transformLineheight('s1-10'),\n },\n} as const;\n\nexport { typo, mobileTypo };\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- export { GYM_PARTNER_REGISTRATION_GOLD_POLICY, GymPartnerRegistrationGoldInput, GymPartnerRegistrationGoldResult, MIN_DOWNLOAD_SECONDS, ORIGINAL_DOWNLOAD_COST_POLICY, OriginalDownloadCostOptions, calculateGymPartnerRegistrationGold, calculateOriginalDownloadCostWon, isDownloadDurationAllowed } from './libs.cjs';
1
+ export { GYM_PARTNER_REGISTRATION_GOLD_POLICY, GymPartnerRegistrationGoldInput, GymPartnerRegistrationGoldResult, GymPartnerRegistrationGoldTotalItem, GymPartnerRegistrationGoldTotalResult, MIN_DOWNLOAD_SECONDS, ORIGINAL_DOWNLOAD_COST_POLICY, OriginalDownloadCostOptions, calculateGymPartnerRegistrationGold, calculateGymPartnerRegistrationGoldTotal, calculateOriginalDownloadCostWon, isDownloadDurationAllowed } from './libs.cjs';
2
2
  export { T as TicketCode } from './membership-C_ziSHS0.cjs';
3
3
  export { APIErrorResponse, APIResponse, APIResponseBase } from './types.cjs';
4
4
  export { color, colorV2, mobileTypo, typo } from './styles.cjs';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { GYM_PARTNER_REGISTRATION_GOLD_POLICY, GymPartnerRegistrationGoldInput, GymPartnerRegistrationGoldResult, MIN_DOWNLOAD_SECONDS, ORIGINAL_DOWNLOAD_COST_POLICY, OriginalDownloadCostOptions, calculateGymPartnerRegistrationGold, calculateOriginalDownloadCostWon, isDownloadDurationAllowed } from './libs.js';
1
+ export { GYM_PARTNER_REGISTRATION_GOLD_POLICY, GymPartnerRegistrationGoldInput, GymPartnerRegistrationGoldResult, GymPartnerRegistrationGoldTotalItem, GymPartnerRegistrationGoldTotalResult, MIN_DOWNLOAD_SECONDS, ORIGINAL_DOWNLOAD_COST_POLICY, OriginalDownloadCostOptions, calculateGymPartnerRegistrationGold, calculateGymPartnerRegistrationGoldTotal, calculateOriginalDownloadCostWon, isDownloadDurationAllowed } from './libs.js';
2
2
  export { T as TicketCode } from './membership-C_ziSHS0.js';
3
3
  export { APIErrorResponse, APIResponse, APIResponseBase } from './types.js';
4
4
  export { color, colorV2, mobileTypo, typo } from './styles.js';
package/dist/index.js CHANGED
@@ -62,6 +62,20 @@ function calculateGymPartnerRegistrationGold(input) {
62
62
  goldAmount: days * GYM_PARTNER_REGISTRATION_GOLD_POLICY.goldPerDay
63
63
  };
64
64
  }
65
+ function calculateGymPartnerRegistrationGoldTotal(periods) {
66
+ const items = periods.map((period) => {
67
+ const result = calculateGymPartnerRegistrationGold(period);
68
+ return {
69
+ ...period,
70
+ ...result
71
+ };
72
+ });
73
+ return {
74
+ totalDays: items.reduce((sum, item) => sum + item.days, 0),
75
+ totalGoldAmount: items.reduce((sum, item) => sum + item.goldAmount, 0),
76
+ items
77
+ };
78
+ }
65
79
  function parseKstDateString(input, fieldName) {
66
80
  const dateOnlyMatch = DATE_ONLY_PATTERN.exec(input);
67
81
  if (dateOnlyMatch === null) {
@@ -328,6 +342,7 @@ export {
328
342
  MIN_DOWNLOAD_SECONDS,
329
343
  ORIGINAL_DOWNLOAD_COST_POLICY,
330
344
  calculateGymPartnerRegistrationGold,
345
+ calculateGymPartnerRegistrationGoldTotal,
331
346
  calculateOriginalDownloadCostWon,
332
347
  color,
333
348
  colorV2,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/libs/cost.ts","../src/libs/gym-registration-gold.ts","../src/styles/color.ts","../src/styles/typo.ts"],"sourcesContent":["import type { TicketCode } from '../types/membership';\n\nexport const ORIGINAL_DOWNLOAD_COST_POLICY = {\n baseDurationSeconds: 600,\n baseCostWon: 2000,\n fullVideoDiscountRate: 0.1,\n /**\n * v14 신정책: 멤버십 plan별 분당 KRW 요율.\n * `calculateOriginalDownloadCostWon(duration, { plan })`로 호출 시 적용된다.\n * plan 미지정 시 `baseCostWon/baseDurationSeconds` 기반 단일 단가(레거시 200원/분)로 폴백 — 기존 호출자 호환.\n */\n perPlanKrwPerMinute: {\n FREE: 100,\n PLUS: 50,\n PRO: 25,\n },\n /**\n * 멤버십 plan별 무료 다운로드 구간(초).\n * 다운로드 길이 중 앞 `freeSecondsByPlan[plan]`초는 과금에서 제외하고,\n * 이를 초과한 구간에만 `perPlanKrwPerMinute[plan]` 요율을 적용한다 (초과분 과금).\n * plan 미지정(레거시 단일 단가) 경로에는 무료 구간을 적용하지 않는다.\n */\n freeSecondsByPlan: {\n FREE: 10,\n PLUS: 20,\n PRO: 30,\n },\n} as const;\n\n/**\n * 다운로드 가능한 최소 길이(초).\n * 이보다 짧은 구간은 다운로드 요청 자체를 허용하지 않는다 (server·FE 공용 제약).\n */\nexport const MIN_DOWNLOAD_SECONDS = 10;\n\n/**\n * 다운로드 요청 길이가 허용되는지 검사한다.\n * `durationSeconds >= MIN_DOWNLOAD_SECONDS`일 때만 true.\n */\nexport function isDownloadDurationAllowed(durationSeconds: number): boolean {\n return (\n Number.isFinite(durationSeconds) && durationSeconds >= MIN_DOWNLOAD_SECONDS\n );\n}\n\nexport interface OriginalDownloadCostOptions {\n isFullVideo?: boolean;\n /**\n * 멤버십 plan. 지정 시 `perPlanKrwPerMinute[plan]`을 단가로,\n * `freeSecondsByPlan[plan]`을 무료 구간으로 적용한다.\n * 미지정(undefined) 시 레거시 단일 단가(`baseCostWon/baseDurationSeconds`) + 무료 구간 없음.\n */\n plan?: TicketCode;\n}\n\nexport function calculateOriginalDownloadCostWon(\n durationSeconds: number,\n options: OriginalDownloadCostOptions = {},\n): number {\n if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) return 0;\n\n const { plan, isFullVideo } = options;\n\n // plan 지정 시 앞 freeSeconds 구간은 과금에서 제외하고 초과분만 과금한다.\n // 레거시 경로(plan 미지정)는 무료 구간 0으로 기존 동작을 유지한다.\n const freeSeconds =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.freeSecondsByPlan[plan]\n : 0;\n const billableSeconds = Math.max(0, durationSeconds - freeSeconds);\n if (billableSeconds <= 0) return 0;\n\n const unitPricePerSecond =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.perPlanKrwPerMinute[plan] / 60\n : ORIGINAL_DOWNLOAD_COST_POLICY.baseCostWon /\n ORIGINAL_DOWNLOAD_COST_POLICY.baseDurationSeconds;\n\n const discountMultiplier = isFullVideo\n ? 1 - ORIGINAL_DOWNLOAD_COST_POLICY.fullVideoDiscountRate\n : 1;\n\n // 부동소수점 drift(예: 30.000000000000004) 때문에 1원 과다 청구되는 것을 막기 위해\n // 원 단위 미만 노이즈를 보정한 뒤 올림한다.\n const rawCost = billableSeconds * unitPricePerSecond * discountMultiplier;\n return Math.ceil(Number(rawCost.toFixed(6)));\n}\n","export const GYM_PARTNER_REGISTRATION_GOLD_POLICY = {\n goldPerDay: 350,\n timeZone: 'Asia/Seoul',\n} as const;\n\nexport interface GymPartnerRegistrationGoldInput {\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n startDate: string;\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n endDate: string;\n}\n\nexport interface GymPartnerRegistrationGoldResult {\n days: number;\n goldAmount: number;\n}\n\ninterface KstDateParts {\n year: number;\n month: number;\n day: number;\n}\n\nconst DATE_ONLY_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\n\nexport function calculateGymPartnerRegistrationGold(\n input: GymPartnerRegistrationGoldInput,\n): GymPartnerRegistrationGoldResult {\n const startDate = parseKstDateString(input.startDate, 'startDate');\n const endDate = parseKstDateString(input.endDate, 'endDate');\n\n const startDateKey = toUtcDateKey(startDate);\n const endDateKey = toUtcDateKey(endDate);\n\n if (endDateKey < startDateKey) {\n throw new RangeError('endDate must be on or after startDate');\n }\n\n const days = Math.floor((endDateKey - startDateKey) / MS_PER_DAY) + 1;\n\n return {\n days,\n goldAmount: days * GYM_PARTNER_REGISTRATION_GOLD_POLICY.goldPerDay,\n };\n}\n\nfunction parseKstDateString(\n input: string,\n fieldName: 'startDate' | 'endDate',\n): KstDateParts {\n const dateOnlyMatch = DATE_ONLY_PATTERN.exec(input);\n if (dateOnlyMatch === null) {\n throw new RangeError(`${fieldName} must be a YYYY-MM-DD date`);\n }\n\n const year = Number(dateOnlyMatch[1]);\n const month = Number(dateOnlyMatch[2]);\n const day = Number(dateOnlyMatch[3]);\n const utcDate = new Date(Date.UTC(year, month - 1, day));\n\n if (\n utcDate.getUTCFullYear() !== year ||\n utcDate.getUTCMonth() + 1 !== month ||\n utcDate.getUTCDate() !== day\n ) {\n throw new RangeError(`${fieldName} must be a valid date`);\n }\n\n return { year, month, day };\n}\n\nfunction toUtcDateKey(date: KstDateParts): number {\n return Date.UTC(date.year, date.month - 1, date.day);\n}\n","const color = {\n primary: '#242535',\n primaryB: '#515265',\n primary2: '#9335FB',\n primary2B: '#6323AA',\n secondary: '#FADD32',\n bgColor: '#F3F4F6',\n error: '#EA2E2E',\n\n white: '#FFFFFF',\n black: '#111111',\n gray1: '#424242',\n gray2: '#616161',\n gray3: '#8E8E8E',\n gray4: '#BDBDBD',\n gray5: '#E0E0E0',\n gray6: '#EDEDED',\n\n sub: '#A3A4BD',\n sub3: '#5561E8',\n sub3b: '#F1F7FF',\n sub4: '#1D1A1A',\n\n point: '#FADD32',\n} as const;\n\n/**\n * @deprecated use color instead (작명 실수 ㅠ)\n */\n\nconst colorV2 = color;\n\nexport { colorV2, color };\n","type TypographyKey =\n | 'h1-24b'\n | 'h1-24m'\n | 'h1-24'\n | 'h2-20b'\n | 'h2-20m'\n | 'h2-20'\n | 'h3-18b'\n | 'h3-18m'\n | 'h3-18'\n | 'b1-16b'\n | 'b1-16m'\n | 'b1-16'\n | 'b2-14b'\n | 'b2-14m'\n | 'b2-14'\n | 'b3-12b'\n | 'b3-12m'\n | 'b3-12'\n | 's1-10b'\n | 's1-10m'\n | 's1-10';\n\nconst typo = {\n 'h1-24b': {\n fontSize: 24,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h1-24m': {\n fontSize: 24,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h1-24': {\n fontSize: 24,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 'h2-20b': {\n fontSize: 20,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h2-20m': {\n fontSize: 20,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h2-20': {\n fontSize: 20,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h3-18b': {\n fontSize: 18,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h3-18m': {\n fontSize: 18,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h3-18': {\n fontSize: 18,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b1-16b': {\n fontSize: 16,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b1-16m': {\n fontSize: 16,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b1-16': {\n fontSize: 16,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 'b2-14b': {\n fontSize: 14,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b2-14m': {\n fontSize: 14,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b2-14': {\n fontSize: 14,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 'b3-12b': {\n fontSize: 12,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b3-12m': {\n fontSize: 12,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b3-12': {\n fontSize: 12,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 's1-10b': {\n fontSize: 10,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 's1-10m': {\n fontSize: 10,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 's1-10': {\n fontSize: 10,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n} as const;\n\nconst transformLineheight = (key: TypographyKey) => {\n return typo[key].lineHeight * typo[key].fontSize;\n};\n\nconst mobileTypo = {\n 'h1-24b': {\n ...typo['h1-24b'],\n lineHeight: transformLineheight('h1-24b'),\n },\n\n 'h1-24m': {\n ...typo['h1-24m'],\n lineHeight: transformLineheight('h1-24m'),\n },\n\n 'h1-24': {\n ...typo['h1-24'],\n lineHeight: transformLineheight('h1-24'),\n },\n\n 'h2-20b': {\n ...typo['h2-20b'],\n lineHeight: transformLineheight('h2-20b'),\n },\n\n 'h2-20m': {\n ...typo['h2-20m'],\n lineHeight: transformLineheight('h2-20m'),\n },\n\n 'h2-20': {\n ...typo['h2-20'],\n lineHeight: transformLineheight('h2-20'),\n },\n\n 'h3-18b': {\n ...typo['h3-18b'],\n lineHeight: transformLineheight('h3-18b'),\n },\n\n 'h3-18m': {\n ...typo['h3-18m'],\n lineHeight: transformLineheight('h3-18m'),\n },\n\n 'h3-18': {\n ...typo['h3-18'],\n lineHeight: transformLineheight('h3-18'),\n },\n\n 'b1-16b': {\n ...typo['b1-16b'],\n lineHeight: transformLineheight('b1-16b'),\n },\n\n 'b1-16m': {\n ...typo['b1-16m'],\n lineHeight: transformLineheight('b1-16m'),\n },\n\n 'b1-16': {\n ...typo['b1-16'],\n lineHeight: transformLineheight('b1-16'),\n },\n\n 'b2-14b': {\n ...typo['b2-14b'],\n lineHeight: transformLineheight('b2-14b'),\n },\n\n 'b2-14m': {\n ...typo['b2-14m'],\n lineHeight: transformLineheight('b2-14m'),\n },\n\n 'b2-14': {\n ...typo['b2-14'],\n lineHeight: transformLineheight('b2-14'),\n },\n\n 'b3-12b': {\n ...typo['b3-12b'],\n lineHeight: transformLineheight('b3-12b'),\n },\n\n 'b3-12m': {\n ...typo['b3-12m'],\n lineHeight: transformLineheight('b3-12m'),\n },\n\n 'b3-12': {\n ...typo['b3-12'],\n lineHeight: transformLineheight('b3-12'),\n },\n\n 's1-10b': {\n ...typo['s1-10b'],\n lineHeight: transformLineheight('s1-10b'),\n },\n\n 's1-10m': {\n ...typo['s1-10m'],\n lineHeight: transformLineheight('s1-10m'),\n },\n\n 's1-10': {\n ...typo['s1-10'],\n lineHeight: transformLineheight('s1-10'),\n },\n} as const;\n\nexport { typo, mobileTypo };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/libs/cost.ts","../src/libs/gym-registration-gold.ts","../src/styles/color.ts","../src/styles/typo.ts"],"sourcesContent":["import type { TicketCode } from '../types/membership';\n\nexport const ORIGINAL_DOWNLOAD_COST_POLICY = {\n baseDurationSeconds: 600,\n baseCostWon: 2000,\n fullVideoDiscountRate: 0.1,\n /**\n * v14 신정책: 멤버십 plan별 분당 KRW 요율.\n * `calculateOriginalDownloadCostWon(duration, { plan })`로 호출 시 적용된다.\n * plan 미지정 시 `baseCostWon/baseDurationSeconds` 기반 단일 단가(레거시 200원/분)로 폴백 — 기존 호출자 호환.\n */\n perPlanKrwPerMinute: {\n FREE: 100,\n PLUS: 50,\n PRO: 25,\n },\n /**\n * 멤버십 plan별 무료 다운로드 구간(초).\n * 다운로드 길이 중 앞 `freeSecondsByPlan[plan]`초는 과금에서 제외하고,\n * 이를 초과한 구간에만 `perPlanKrwPerMinute[plan]` 요율을 적용한다 (초과분 과금).\n * plan 미지정(레거시 단일 단가) 경로에는 무료 구간을 적용하지 않는다.\n */\n freeSecondsByPlan: {\n FREE: 10,\n PLUS: 20,\n PRO: 30,\n },\n} as const;\n\n/**\n * 다운로드 가능한 최소 길이(초).\n * 이보다 짧은 구간은 다운로드 요청 자체를 허용하지 않는다 (server·FE 공용 제약).\n */\nexport const MIN_DOWNLOAD_SECONDS = 10;\n\n/**\n * 다운로드 요청 길이가 허용되는지 검사한다.\n * `durationSeconds >= MIN_DOWNLOAD_SECONDS`일 때만 true.\n */\nexport function isDownloadDurationAllowed(durationSeconds: number): boolean {\n return (\n Number.isFinite(durationSeconds) && durationSeconds >= MIN_DOWNLOAD_SECONDS\n );\n}\n\nexport interface OriginalDownloadCostOptions {\n isFullVideo?: boolean;\n /**\n * 멤버십 plan. 지정 시 `perPlanKrwPerMinute[plan]`을 단가로,\n * `freeSecondsByPlan[plan]`을 무료 구간으로 적용한다.\n * 미지정(undefined) 시 레거시 단일 단가(`baseCostWon/baseDurationSeconds`) + 무료 구간 없음.\n */\n plan?: TicketCode;\n}\n\nexport function calculateOriginalDownloadCostWon(\n durationSeconds: number,\n options: OriginalDownloadCostOptions = {},\n): number {\n if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) return 0;\n\n const { plan, isFullVideo } = options;\n\n // plan 지정 시 앞 freeSeconds 구간은 과금에서 제외하고 초과분만 과금한다.\n // 레거시 경로(plan 미지정)는 무료 구간 0으로 기존 동작을 유지한다.\n const freeSeconds =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.freeSecondsByPlan[plan]\n : 0;\n const billableSeconds = Math.max(0, durationSeconds - freeSeconds);\n if (billableSeconds <= 0) return 0;\n\n const unitPricePerSecond =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.perPlanKrwPerMinute[plan] / 60\n : ORIGINAL_DOWNLOAD_COST_POLICY.baseCostWon /\n ORIGINAL_DOWNLOAD_COST_POLICY.baseDurationSeconds;\n\n const discountMultiplier = isFullVideo\n ? 1 - ORIGINAL_DOWNLOAD_COST_POLICY.fullVideoDiscountRate\n : 1;\n\n // 부동소수점 drift(예: 30.000000000000004) 때문에 1원 과다 청구되는 것을 막기 위해\n // 원 단위 미만 노이즈를 보정한 뒤 올림한다.\n const rawCost = billableSeconds * unitPricePerSecond * discountMultiplier;\n return Math.ceil(Number(rawCost.toFixed(6)));\n}\n","export const GYM_PARTNER_REGISTRATION_GOLD_POLICY = {\n goldPerDay: 350,\n timeZone: 'Asia/Seoul',\n} as const;\n\nexport interface GymPartnerRegistrationGoldInput {\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n startDate: string;\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n endDate: string;\n}\n\nexport interface GymPartnerRegistrationGoldResult {\n days: number;\n goldAmount: number;\n}\n\nexport interface GymPartnerRegistrationGoldTotalItem\n extends GymPartnerRegistrationGoldInput,\n GymPartnerRegistrationGoldResult {}\n\nexport interface GymPartnerRegistrationGoldTotalResult {\n totalDays: number;\n totalGoldAmount: number;\n items: GymPartnerRegistrationGoldTotalItem[];\n}\n\ninterface KstDateParts {\n year: number;\n month: number;\n day: number;\n}\n\nconst DATE_ONLY_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\n\nexport function calculateGymPartnerRegistrationGold(\n input: GymPartnerRegistrationGoldInput,\n): GymPartnerRegistrationGoldResult {\n const startDate = parseKstDateString(input.startDate, 'startDate');\n const endDate = parseKstDateString(input.endDate, 'endDate');\n\n const startDateKey = toUtcDateKey(startDate);\n const endDateKey = toUtcDateKey(endDate);\n\n if (endDateKey < startDateKey) {\n throw new RangeError('endDate must be on or after startDate');\n }\n\n const days = Math.floor((endDateKey - startDateKey) / MS_PER_DAY) + 1;\n\n return {\n days,\n goldAmount: days * GYM_PARTNER_REGISTRATION_GOLD_POLICY.goldPerDay,\n };\n}\n\nexport function calculateGymPartnerRegistrationGoldTotal(\n periods: GymPartnerRegistrationGoldInput[],\n): GymPartnerRegistrationGoldTotalResult {\n const items = periods.map((period) => {\n const result = calculateGymPartnerRegistrationGold(period);\n\n return {\n ...period,\n ...result,\n };\n });\n\n return {\n totalDays: items.reduce((sum, item) => sum + item.days, 0),\n totalGoldAmount: items.reduce((sum, item) => sum + item.goldAmount, 0),\n items,\n };\n}\n\nfunction parseKstDateString(\n input: string,\n fieldName: 'startDate' | 'endDate',\n): KstDateParts {\n const dateOnlyMatch = DATE_ONLY_PATTERN.exec(input);\n if (dateOnlyMatch === null) {\n throw new RangeError(`${fieldName} must be a YYYY-MM-DD date`);\n }\n\n const year = Number(dateOnlyMatch[1]);\n const month = Number(dateOnlyMatch[2]);\n const day = Number(dateOnlyMatch[3]);\n const utcDate = new Date(Date.UTC(year, month - 1, day));\n\n if (\n utcDate.getUTCFullYear() !== year ||\n utcDate.getUTCMonth() + 1 !== month ||\n utcDate.getUTCDate() !== day\n ) {\n throw new RangeError(`${fieldName} must be a valid date`);\n }\n\n return { year, month, day };\n}\n\nfunction toUtcDateKey(date: KstDateParts): number {\n return Date.UTC(date.year, date.month - 1, date.day);\n}\n","const color = {\n primary: '#242535',\n primaryB: '#515265',\n primary2: '#9335FB',\n primary2B: '#6323AA',\n secondary: '#FADD32',\n bgColor: '#F3F4F6',\n error: '#EA2E2E',\n\n white: '#FFFFFF',\n black: '#111111',\n gray1: '#424242',\n gray2: '#616161',\n gray3: '#8E8E8E',\n gray4: '#BDBDBD',\n gray5: '#E0E0E0',\n gray6: '#EDEDED',\n\n sub: '#A3A4BD',\n sub3: '#5561E8',\n sub3b: '#F1F7FF',\n sub4: '#1D1A1A',\n\n point: '#FADD32',\n} as const;\n\n/**\n * @deprecated use color instead (작명 실수 ㅠ)\n */\n\nconst colorV2 = color;\n\nexport { colorV2, color };\n","type TypographyKey =\n | 'h1-24b'\n | 'h1-24m'\n | 'h1-24'\n | 'h2-20b'\n | 'h2-20m'\n | 'h2-20'\n | 'h3-18b'\n | 'h3-18m'\n | 'h3-18'\n | 'b1-16b'\n | 'b1-16m'\n | 'b1-16'\n | 'b2-14b'\n | 'b2-14m'\n | 'b2-14'\n | 'b3-12b'\n | 'b3-12m'\n | 'b3-12'\n | 's1-10b'\n | 's1-10m'\n | 's1-10';\n\nconst typo = {\n 'h1-24b': {\n fontSize: 24,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h1-24m': {\n fontSize: 24,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h1-24': {\n fontSize: 24,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 'h2-20b': {\n fontSize: 20,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h2-20m': {\n fontSize: 20,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h2-20': {\n fontSize: 20,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h3-18b': {\n fontSize: 18,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h3-18m': {\n fontSize: 18,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'h3-18': {\n fontSize: 18,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b1-16b': {\n fontSize: 16,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b1-16m': {\n fontSize: 16,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b1-16': {\n fontSize: 16,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 'b2-14b': {\n fontSize: 14,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b2-14m': {\n fontSize: 14,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b2-14': {\n fontSize: 14,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 'b3-12b': {\n fontSize: 12,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b3-12m': {\n fontSize: 12,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 'b3-12': {\n fontSize: 12,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n\n 's1-10b': {\n fontSize: 10,\n fontWeight: 700,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 's1-10m': {\n fontSize: 10,\n fontWeight: 500,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n 's1-10': {\n fontSize: 10,\n fontWeight: 400,\n lineHeight: 1.5,\n letterSpacing: 0,\n },\n} as const;\n\nconst transformLineheight = (key: TypographyKey) => {\n return typo[key].lineHeight * typo[key].fontSize;\n};\n\nconst mobileTypo = {\n 'h1-24b': {\n ...typo['h1-24b'],\n lineHeight: transformLineheight('h1-24b'),\n },\n\n 'h1-24m': {\n ...typo['h1-24m'],\n lineHeight: transformLineheight('h1-24m'),\n },\n\n 'h1-24': {\n ...typo['h1-24'],\n lineHeight: transformLineheight('h1-24'),\n },\n\n 'h2-20b': {\n ...typo['h2-20b'],\n lineHeight: transformLineheight('h2-20b'),\n },\n\n 'h2-20m': {\n ...typo['h2-20m'],\n lineHeight: transformLineheight('h2-20m'),\n },\n\n 'h2-20': {\n ...typo['h2-20'],\n lineHeight: transformLineheight('h2-20'),\n },\n\n 'h3-18b': {\n ...typo['h3-18b'],\n lineHeight: transformLineheight('h3-18b'),\n },\n\n 'h3-18m': {\n ...typo['h3-18m'],\n lineHeight: transformLineheight('h3-18m'),\n },\n\n 'h3-18': {\n ...typo['h3-18'],\n lineHeight: transformLineheight('h3-18'),\n },\n\n 'b1-16b': {\n ...typo['b1-16b'],\n lineHeight: transformLineheight('b1-16b'),\n },\n\n 'b1-16m': {\n ...typo['b1-16m'],\n lineHeight: transformLineheight('b1-16m'),\n },\n\n 'b1-16': {\n ...typo['b1-16'],\n lineHeight: transformLineheight('b1-16'),\n },\n\n 'b2-14b': {\n ...typo['b2-14b'],\n lineHeight: transformLineheight('b2-14b'),\n },\n\n 'b2-14m': {\n ...typo['b2-14m'],\n lineHeight: transformLineheight('b2-14m'),\n },\n\n 'b2-14': {\n ...typo['b2-14'],\n lineHeight: transformLineheight('b2-14'),\n },\n\n 'b3-12b': {\n ...typo['b3-12b'],\n lineHeight: transformLineheight('b3-12b'),\n },\n\n 'b3-12m': {\n ...typo['b3-12m'],\n lineHeight: transformLineheight('b3-12m'),\n },\n\n 'b3-12': {\n ...typo['b3-12'],\n lineHeight: transformLineheight('b3-12'),\n },\n\n 's1-10b': {\n ...typo['s1-10b'],\n lineHeight: transformLineheight('s1-10b'),\n },\n\n 's1-10m': {\n ...typo['s1-10m'],\n lineHeight: transformLineheight('s1-10m'),\n },\n\n 's1-10': {\n ...typo['s1-10'],\n lineHeight: transformLineheight('s1-10'),\n },\n} as const;\n\nexport { typo, mobileTypo };\n"],"mappings":"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","names":[]}
package/dist/libs.cjs CHANGED
@@ -24,6 +24,7 @@ __export(libs_exports, {
24
24
  MIN_DOWNLOAD_SECONDS: () => MIN_DOWNLOAD_SECONDS,
25
25
  ORIGINAL_DOWNLOAD_COST_POLICY: () => ORIGINAL_DOWNLOAD_COST_POLICY,
26
26
  calculateGymPartnerRegistrationGold: () => calculateGymPartnerRegistrationGold,
27
+ calculateGymPartnerRegistrationGoldTotal: () => calculateGymPartnerRegistrationGoldTotal,
27
28
  calculateOriginalDownloadCostWon: () => calculateOriginalDownloadCostWon,
28
29
  isDownloadDurationAllowed: () => isDownloadDurationAllowed
29
30
  });
@@ -93,6 +94,20 @@ function calculateGymPartnerRegistrationGold(input) {
93
94
  goldAmount: days * GYM_PARTNER_REGISTRATION_GOLD_POLICY.goldPerDay
94
95
  };
95
96
  }
97
+ function calculateGymPartnerRegistrationGoldTotal(periods) {
98
+ const items = periods.map((period) => {
99
+ const result = calculateGymPartnerRegistrationGold(period);
100
+ return {
101
+ ...period,
102
+ ...result
103
+ };
104
+ });
105
+ return {
106
+ totalDays: items.reduce((sum, item) => sum + item.days, 0),
107
+ totalGoldAmount: items.reduce((sum, item) => sum + item.goldAmount, 0),
108
+ items
109
+ };
110
+ }
96
111
  function parseKstDateString(input, fieldName) {
97
112
  const dateOnlyMatch = DATE_ONLY_PATTERN.exec(input);
98
113
  if (dateOnlyMatch === null) {
@@ -116,6 +131,7 @@ function toUtcDateKey(date) {
116
131
  MIN_DOWNLOAD_SECONDS,
117
132
  ORIGINAL_DOWNLOAD_COST_POLICY,
118
133
  calculateGymPartnerRegistrationGold,
134
+ calculateGymPartnerRegistrationGoldTotal,
119
135
  calculateOriginalDownloadCostWon,
120
136
  isDownloadDurationAllowed
121
137
  });
package/dist/libs.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/libs/index.ts","../src/libs/cost.ts","../src/libs/gym-registration-gold.ts"],"sourcesContent":["export * from './cost';\nexport * from './gym-registration-gold';\n","import type { TicketCode } from '../types/membership';\n\nexport const ORIGINAL_DOWNLOAD_COST_POLICY = {\n baseDurationSeconds: 600,\n baseCostWon: 2000,\n fullVideoDiscountRate: 0.1,\n /**\n * v14 신정책: 멤버십 plan별 분당 KRW 요율.\n * `calculateOriginalDownloadCostWon(duration, { plan })`로 호출 시 적용된다.\n * plan 미지정 시 `baseCostWon/baseDurationSeconds` 기반 단일 단가(레거시 200원/분)로 폴백 — 기존 호출자 호환.\n */\n perPlanKrwPerMinute: {\n FREE: 100,\n PLUS: 50,\n PRO: 25,\n },\n /**\n * 멤버십 plan별 무료 다운로드 구간(초).\n * 다운로드 길이 중 앞 `freeSecondsByPlan[plan]`초는 과금에서 제외하고,\n * 이를 초과한 구간에만 `perPlanKrwPerMinute[plan]` 요율을 적용한다 (초과분 과금).\n * plan 미지정(레거시 단일 단가) 경로에는 무료 구간을 적용하지 않는다.\n */\n freeSecondsByPlan: {\n FREE: 10,\n PLUS: 20,\n PRO: 30,\n },\n} as const;\n\n/**\n * 다운로드 가능한 최소 길이(초).\n * 이보다 짧은 구간은 다운로드 요청 자체를 허용하지 않는다 (server·FE 공용 제약).\n */\nexport const MIN_DOWNLOAD_SECONDS = 10;\n\n/**\n * 다운로드 요청 길이가 허용되는지 검사한다.\n * `durationSeconds >= MIN_DOWNLOAD_SECONDS`일 때만 true.\n */\nexport function isDownloadDurationAllowed(durationSeconds: number): boolean {\n return (\n Number.isFinite(durationSeconds) && durationSeconds >= MIN_DOWNLOAD_SECONDS\n );\n}\n\nexport interface OriginalDownloadCostOptions {\n isFullVideo?: boolean;\n /**\n * 멤버십 plan. 지정 시 `perPlanKrwPerMinute[plan]`을 단가로,\n * `freeSecondsByPlan[plan]`을 무료 구간으로 적용한다.\n * 미지정(undefined) 시 레거시 단일 단가(`baseCostWon/baseDurationSeconds`) + 무료 구간 없음.\n */\n plan?: TicketCode;\n}\n\nexport function calculateOriginalDownloadCostWon(\n durationSeconds: number,\n options: OriginalDownloadCostOptions = {},\n): number {\n if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) return 0;\n\n const { plan, isFullVideo } = options;\n\n // plan 지정 시 앞 freeSeconds 구간은 과금에서 제외하고 초과분만 과금한다.\n // 레거시 경로(plan 미지정)는 무료 구간 0으로 기존 동작을 유지한다.\n const freeSeconds =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.freeSecondsByPlan[plan]\n : 0;\n const billableSeconds = Math.max(0, durationSeconds - freeSeconds);\n if (billableSeconds <= 0) return 0;\n\n const unitPricePerSecond =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.perPlanKrwPerMinute[plan] / 60\n : ORIGINAL_DOWNLOAD_COST_POLICY.baseCostWon /\n ORIGINAL_DOWNLOAD_COST_POLICY.baseDurationSeconds;\n\n const discountMultiplier = isFullVideo\n ? 1 - ORIGINAL_DOWNLOAD_COST_POLICY.fullVideoDiscountRate\n : 1;\n\n // 부동소수점 drift(예: 30.000000000000004) 때문에 1원 과다 청구되는 것을 막기 위해\n // 원 단위 미만 노이즈를 보정한 뒤 올림한다.\n const rawCost = billableSeconds * unitPricePerSecond * discountMultiplier;\n return Math.ceil(Number(rawCost.toFixed(6)));\n}\n","export const GYM_PARTNER_REGISTRATION_GOLD_POLICY = {\n goldPerDay: 350,\n timeZone: 'Asia/Seoul',\n} as const;\n\nexport interface GymPartnerRegistrationGoldInput {\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n startDate: string;\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n endDate: string;\n}\n\nexport interface GymPartnerRegistrationGoldResult {\n days: number;\n goldAmount: number;\n}\n\ninterface KstDateParts {\n year: number;\n month: number;\n day: number;\n}\n\nconst DATE_ONLY_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\n\nexport function calculateGymPartnerRegistrationGold(\n input: GymPartnerRegistrationGoldInput,\n): GymPartnerRegistrationGoldResult {\n const startDate = parseKstDateString(input.startDate, 'startDate');\n const endDate = parseKstDateString(input.endDate, 'endDate');\n\n const startDateKey = toUtcDateKey(startDate);\n const endDateKey = toUtcDateKey(endDate);\n\n if (endDateKey < startDateKey) {\n throw new RangeError('endDate must be on or after startDate');\n }\n\n const days = Math.floor((endDateKey - startDateKey) / MS_PER_DAY) + 1;\n\n return {\n days,\n goldAmount: days * GYM_PARTNER_REGISTRATION_GOLD_POLICY.goldPerDay,\n };\n}\n\nfunction parseKstDateString(\n input: string,\n fieldName: 'startDate' | 'endDate',\n): KstDateParts {\n const dateOnlyMatch = DATE_ONLY_PATTERN.exec(input);\n if (dateOnlyMatch === null) {\n throw new RangeError(`${fieldName} must be a YYYY-MM-DD date`);\n }\n\n const year = Number(dateOnlyMatch[1]);\n const month = Number(dateOnlyMatch[2]);\n const day = Number(dateOnlyMatch[3]);\n const utcDate = new Date(Date.UTC(year, month - 1, day));\n\n if (\n utcDate.getUTCFullYear() !== year ||\n utcDate.getUTCMonth() + 1 !== month ||\n utcDate.getUTCDate() !== day\n ) {\n throw new RangeError(`${fieldName} must be a valid date`);\n }\n\n return { year, month, day };\n}\n\nfunction toUtcDateKey(date: KstDateParts): number {\n return Date.UTC(date.year, date.month - 1, date.day);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEO,IAAM,gCAAgC;AAAA,EAC3C,qBAAqB;AAAA,EACrB,aAAa;AAAA,EACb,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvB,qBAAqB;AAAA,IACnB,MAAM;AAAA,IACN,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,mBAAmB;AAAA,IACjB,MAAM;AAAA,IACN,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AACF;AAMO,IAAM,uBAAuB;AAM7B,SAAS,0BAA0B,iBAAkC;AAC1E,SACE,OAAO,SAAS,eAAe,KAAK,mBAAmB;AAE3D;AAYO,SAAS,iCACd,iBACA,UAAuC,CAAC,GAChC;AACR,MAAI,CAAC,OAAO,SAAS,eAAe,KAAK,mBAAmB,EAAG,QAAO;AAEtE,QAAM,EAAE,MAAM,YAAY,IAAI;AAI9B,QAAM,cACJ,SAAS,SACL,8BAA8B,kBAAkB,IAAI,IACpD;AACN,QAAM,kBAAkB,KAAK,IAAI,GAAG,kBAAkB,WAAW;AACjE,MAAI,mBAAmB,EAAG,QAAO;AAEjC,QAAM,qBACJ,SAAS,SACL,8BAA8B,oBAAoB,IAAI,IAAI,KAC1D,8BAA8B,cAC9B,8BAA8B;AAEpC,QAAM,qBAAqB,cACvB,IAAI,8BAA8B,wBAClC;AAIJ,QAAM,UAAU,kBAAkB,qBAAqB;AACvD,SAAO,KAAK,KAAK,OAAO,QAAQ,QAAQ,CAAC,CAAC,CAAC;AAC7C;;;ACtFO,IAAM,uCAAuC;AAAA,EAClD,YAAY;AAAA,EACZ,UAAU;AACZ;AAwBA,IAAM,oBAAoB;AAC1B,IAAM,aAAa,KAAK,KAAK,KAAK;AAE3B,SAAS,oCACd,OACkC;AAClC,QAAM,YAAY,mBAAmB,MAAM,WAAW,WAAW;AACjE,QAAM,UAAU,mBAAmB,MAAM,SAAS,SAAS;AAE3D,QAAM,eAAe,aAAa,SAAS;AAC3C,QAAM,aAAa,aAAa,OAAO;AAEvC,MAAI,aAAa,cAAc;AAC7B,UAAM,IAAI,WAAW,uCAAuC;AAAA,EAC9D;AAEA,QAAM,OAAO,KAAK,OAAO,aAAa,gBAAgB,UAAU,IAAI;AAEpE,SAAO;AAAA,IACL;AAAA,IACA,YAAY,OAAO,qCAAqC;AAAA,EAC1D;AACF;AAEA,SAAS,mBACP,OACA,WACc;AACd,QAAM,gBAAgB,kBAAkB,KAAK,KAAK;AAClD,MAAI,kBAAkB,MAAM;AAC1B,UAAM,IAAI,WAAW,GAAG,SAAS,4BAA4B;AAAA,EAC/D;AAEA,QAAM,OAAO,OAAO,cAAc,CAAC,CAAC;AACpC,QAAM,QAAQ,OAAO,cAAc,CAAC,CAAC;AACrC,QAAM,MAAM,OAAO,cAAc,CAAC,CAAC;AACnC,QAAM,UAAU,IAAI,KAAK,KAAK,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC;AAEvD,MACE,QAAQ,eAAe,MAAM,QAC7B,QAAQ,YAAY,IAAI,MAAM,SAC9B,QAAQ,WAAW,MAAM,KACzB;AACA,UAAM,IAAI,WAAW,GAAG,SAAS,uBAAuB;AAAA,EAC1D;AAEA,SAAO,EAAE,MAAM,OAAO,IAAI;AAC5B;AAEA,SAAS,aAAa,MAA4B;AAChD,SAAO,KAAK,IAAI,KAAK,MAAM,KAAK,QAAQ,GAAG,KAAK,GAAG;AACrD;","names":[]}
1
+ {"version":3,"sources":["../src/libs/index.ts","../src/libs/cost.ts","../src/libs/gym-registration-gold.ts"],"sourcesContent":["export * from './cost';\nexport * from './gym-registration-gold';\n","import type { TicketCode } from '../types/membership';\n\nexport const ORIGINAL_DOWNLOAD_COST_POLICY = {\n baseDurationSeconds: 600,\n baseCostWon: 2000,\n fullVideoDiscountRate: 0.1,\n /**\n * v14 신정책: 멤버십 plan별 분당 KRW 요율.\n * `calculateOriginalDownloadCostWon(duration, { plan })`로 호출 시 적용된다.\n * plan 미지정 시 `baseCostWon/baseDurationSeconds` 기반 단일 단가(레거시 200원/분)로 폴백 — 기존 호출자 호환.\n */\n perPlanKrwPerMinute: {\n FREE: 100,\n PLUS: 50,\n PRO: 25,\n },\n /**\n * 멤버십 plan별 무료 다운로드 구간(초).\n * 다운로드 길이 중 앞 `freeSecondsByPlan[plan]`초는 과금에서 제외하고,\n * 이를 초과한 구간에만 `perPlanKrwPerMinute[plan]` 요율을 적용한다 (초과분 과금).\n * plan 미지정(레거시 단일 단가) 경로에는 무료 구간을 적용하지 않는다.\n */\n freeSecondsByPlan: {\n FREE: 10,\n PLUS: 20,\n PRO: 30,\n },\n} as const;\n\n/**\n * 다운로드 가능한 최소 길이(초).\n * 이보다 짧은 구간은 다운로드 요청 자체를 허용하지 않는다 (server·FE 공용 제약).\n */\nexport const MIN_DOWNLOAD_SECONDS = 10;\n\n/**\n * 다운로드 요청 길이가 허용되는지 검사한다.\n * `durationSeconds >= MIN_DOWNLOAD_SECONDS`일 때만 true.\n */\nexport function isDownloadDurationAllowed(durationSeconds: number): boolean {\n return (\n Number.isFinite(durationSeconds) && durationSeconds >= MIN_DOWNLOAD_SECONDS\n );\n}\n\nexport interface OriginalDownloadCostOptions {\n isFullVideo?: boolean;\n /**\n * 멤버십 plan. 지정 시 `perPlanKrwPerMinute[plan]`을 단가로,\n * `freeSecondsByPlan[plan]`을 무료 구간으로 적용한다.\n * 미지정(undefined) 시 레거시 단일 단가(`baseCostWon/baseDurationSeconds`) + 무료 구간 없음.\n */\n plan?: TicketCode;\n}\n\nexport function calculateOriginalDownloadCostWon(\n durationSeconds: number,\n options: OriginalDownloadCostOptions = {},\n): number {\n if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) return 0;\n\n const { plan, isFullVideo } = options;\n\n // plan 지정 시 앞 freeSeconds 구간은 과금에서 제외하고 초과분만 과금한다.\n // 레거시 경로(plan 미지정)는 무료 구간 0으로 기존 동작을 유지한다.\n const freeSeconds =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.freeSecondsByPlan[plan]\n : 0;\n const billableSeconds = Math.max(0, durationSeconds - freeSeconds);\n if (billableSeconds <= 0) return 0;\n\n const unitPricePerSecond =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.perPlanKrwPerMinute[plan] / 60\n : ORIGINAL_DOWNLOAD_COST_POLICY.baseCostWon /\n ORIGINAL_DOWNLOAD_COST_POLICY.baseDurationSeconds;\n\n const discountMultiplier = isFullVideo\n ? 1 - ORIGINAL_DOWNLOAD_COST_POLICY.fullVideoDiscountRate\n : 1;\n\n // 부동소수점 drift(예: 30.000000000000004) 때문에 1원 과다 청구되는 것을 막기 위해\n // 원 단위 미만 노이즈를 보정한 뒤 올림한다.\n const rawCost = billableSeconds * unitPricePerSecond * discountMultiplier;\n return Math.ceil(Number(rawCost.toFixed(6)));\n}\n","export const GYM_PARTNER_REGISTRATION_GOLD_POLICY = {\n goldPerDay: 350,\n timeZone: 'Asia/Seoul',\n} as const;\n\nexport interface GymPartnerRegistrationGoldInput {\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n startDate: string;\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n endDate: string;\n}\n\nexport interface GymPartnerRegistrationGoldResult {\n days: number;\n goldAmount: number;\n}\n\nexport interface GymPartnerRegistrationGoldTotalItem\n extends GymPartnerRegistrationGoldInput,\n GymPartnerRegistrationGoldResult {}\n\nexport interface GymPartnerRegistrationGoldTotalResult {\n totalDays: number;\n totalGoldAmount: number;\n items: GymPartnerRegistrationGoldTotalItem[];\n}\n\ninterface KstDateParts {\n year: number;\n month: number;\n day: number;\n}\n\nconst DATE_ONLY_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\n\nexport function calculateGymPartnerRegistrationGold(\n input: GymPartnerRegistrationGoldInput,\n): GymPartnerRegistrationGoldResult {\n const startDate = parseKstDateString(input.startDate, 'startDate');\n const endDate = parseKstDateString(input.endDate, 'endDate');\n\n const startDateKey = toUtcDateKey(startDate);\n const endDateKey = toUtcDateKey(endDate);\n\n if (endDateKey < startDateKey) {\n throw new RangeError('endDate must be on or after startDate');\n }\n\n const days = Math.floor((endDateKey - startDateKey) / MS_PER_DAY) + 1;\n\n return {\n days,\n goldAmount: days * GYM_PARTNER_REGISTRATION_GOLD_POLICY.goldPerDay,\n };\n}\n\nexport function calculateGymPartnerRegistrationGoldTotal(\n periods: GymPartnerRegistrationGoldInput[],\n): GymPartnerRegistrationGoldTotalResult {\n const items = periods.map((period) => {\n const result = calculateGymPartnerRegistrationGold(period);\n\n return {\n ...period,\n ...result,\n };\n });\n\n return {\n totalDays: items.reduce((sum, item) => sum + item.days, 0),\n totalGoldAmount: items.reduce((sum, item) => sum + item.goldAmount, 0),\n items,\n };\n}\n\nfunction parseKstDateString(\n input: string,\n fieldName: 'startDate' | 'endDate',\n): KstDateParts {\n const dateOnlyMatch = DATE_ONLY_PATTERN.exec(input);\n if (dateOnlyMatch === null) {\n throw new RangeError(`${fieldName} must be a YYYY-MM-DD date`);\n }\n\n const year = Number(dateOnlyMatch[1]);\n const month = Number(dateOnlyMatch[2]);\n const day = Number(dateOnlyMatch[3]);\n const utcDate = new Date(Date.UTC(year, month - 1, day));\n\n if (\n utcDate.getUTCFullYear() !== year ||\n utcDate.getUTCMonth() + 1 !== month ||\n utcDate.getUTCDate() !== day\n ) {\n throw new RangeError(`${fieldName} must be a valid date`);\n }\n\n return { year, month, day };\n}\n\nfunction toUtcDateKey(date: KstDateParts): number {\n return Date.UTC(date.year, date.month - 1, date.day);\n}\n"],"mappings":"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","names":[]}
package/dist/libs.d.cts CHANGED
@@ -65,6 +65,14 @@ interface GymPartnerRegistrationGoldResult {
65
65
  days: number;
66
66
  goldAmount: number;
67
67
  }
68
+ interface GymPartnerRegistrationGoldTotalItem extends GymPartnerRegistrationGoldInput, GymPartnerRegistrationGoldResult {
69
+ }
70
+ interface GymPartnerRegistrationGoldTotalResult {
71
+ totalDays: number;
72
+ totalGoldAmount: number;
73
+ items: GymPartnerRegistrationGoldTotalItem[];
74
+ }
68
75
  declare function calculateGymPartnerRegistrationGold(input: GymPartnerRegistrationGoldInput): GymPartnerRegistrationGoldResult;
76
+ declare function calculateGymPartnerRegistrationGoldTotal(periods: GymPartnerRegistrationGoldInput[]): GymPartnerRegistrationGoldTotalResult;
69
77
 
70
- export { GYM_PARTNER_REGISTRATION_GOLD_POLICY, type GymPartnerRegistrationGoldInput, type GymPartnerRegistrationGoldResult, MIN_DOWNLOAD_SECONDS, ORIGINAL_DOWNLOAD_COST_POLICY, type OriginalDownloadCostOptions, calculateGymPartnerRegistrationGold, calculateOriginalDownloadCostWon, isDownloadDurationAllowed };
78
+ export { GYM_PARTNER_REGISTRATION_GOLD_POLICY, type GymPartnerRegistrationGoldInput, type GymPartnerRegistrationGoldResult, type GymPartnerRegistrationGoldTotalItem, type GymPartnerRegistrationGoldTotalResult, MIN_DOWNLOAD_SECONDS, ORIGINAL_DOWNLOAD_COST_POLICY, type OriginalDownloadCostOptions, calculateGymPartnerRegistrationGold, calculateGymPartnerRegistrationGoldTotal, calculateOriginalDownloadCostWon, isDownloadDurationAllowed };
package/dist/libs.d.ts CHANGED
@@ -65,6 +65,14 @@ interface GymPartnerRegistrationGoldResult {
65
65
  days: number;
66
66
  goldAmount: number;
67
67
  }
68
+ interface GymPartnerRegistrationGoldTotalItem extends GymPartnerRegistrationGoldInput, GymPartnerRegistrationGoldResult {
69
+ }
70
+ interface GymPartnerRegistrationGoldTotalResult {
71
+ totalDays: number;
72
+ totalGoldAmount: number;
73
+ items: GymPartnerRegistrationGoldTotalItem[];
74
+ }
68
75
  declare function calculateGymPartnerRegistrationGold(input: GymPartnerRegistrationGoldInput): GymPartnerRegistrationGoldResult;
76
+ declare function calculateGymPartnerRegistrationGoldTotal(periods: GymPartnerRegistrationGoldInput[]): GymPartnerRegistrationGoldTotalResult;
69
77
 
70
- export { GYM_PARTNER_REGISTRATION_GOLD_POLICY, type GymPartnerRegistrationGoldInput, type GymPartnerRegistrationGoldResult, MIN_DOWNLOAD_SECONDS, ORIGINAL_DOWNLOAD_COST_POLICY, type OriginalDownloadCostOptions, calculateGymPartnerRegistrationGold, calculateOriginalDownloadCostWon, isDownloadDurationAllowed };
78
+ export { GYM_PARTNER_REGISTRATION_GOLD_POLICY, type GymPartnerRegistrationGoldInput, type GymPartnerRegistrationGoldResult, type GymPartnerRegistrationGoldTotalItem, type GymPartnerRegistrationGoldTotalResult, MIN_DOWNLOAD_SECONDS, ORIGINAL_DOWNLOAD_COST_POLICY, type OriginalDownloadCostOptions, calculateGymPartnerRegistrationGold, calculateGymPartnerRegistrationGoldTotal, calculateOriginalDownloadCostWon, isDownloadDurationAllowed };
package/dist/libs.js CHANGED
@@ -62,6 +62,20 @@ function calculateGymPartnerRegistrationGold(input) {
62
62
  goldAmount: days * GYM_PARTNER_REGISTRATION_GOLD_POLICY.goldPerDay
63
63
  };
64
64
  }
65
+ function calculateGymPartnerRegistrationGoldTotal(periods) {
66
+ const items = periods.map((period) => {
67
+ const result = calculateGymPartnerRegistrationGold(period);
68
+ return {
69
+ ...period,
70
+ ...result
71
+ };
72
+ });
73
+ return {
74
+ totalDays: items.reduce((sum, item) => sum + item.days, 0),
75
+ totalGoldAmount: items.reduce((sum, item) => sum + item.goldAmount, 0),
76
+ items
77
+ };
78
+ }
65
79
  function parseKstDateString(input, fieldName) {
66
80
  const dateOnlyMatch = DATE_ONLY_PATTERN.exec(input);
67
81
  if (dateOnlyMatch === null) {
@@ -84,6 +98,7 @@ export {
84
98
  MIN_DOWNLOAD_SECONDS,
85
99
  ORIGINAL_DOWNLOAD_COST_POLICY,
86
100
  calculateGymPartnerRegistrationGold,
101
+ calculateGymPartnerRegistrationGoldTotal,
87
102
  calculateOriginalDownloadCostWon,
88
103
  isDownloadDurationAllowed
89
104
  };
package/dist/libs.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/libs/cost.ts","../src/libs/gym-registration-gold.ts"],"sourcesContent":["import type { TicketCode } from '../types/membership';\n\nexport const ORIGINAL_DOWNLOAD_COST_POLICY = {\n baseDurationSeconds: 600,\n baseCostWon: 2000,\n fullVideoDiscountRate: 0.1,\n /**\n * v14 신정책: 멤버십 plan별 분당 KRW 요율.\n * `calculateOriginalDownloadCostWon(duration, { plan })`로 호출 시 적용된다.\n * plan 미지정 시 `baseCostWon/baseDurationSeconds` 기반 단일 단가(레거시 200원/분)로 폴백 — 기존 호출자 호환.\n */\n perPlanKrwPerMinute: {\n FREE: 100,\n PLUS: 50,\n PRO: 25,\n },\n /**\n * 멤버십 plan별 무료 다운로드 구간(초).\n * 다운로드 길이 중 앞 `freeSecondsByPlan[plan]`초는 과금에서 제외하고,\n * 이를 초과한 구간에만 `perPlanKrwPerMinute[plan]` 요율을 적용한다 (초과분 과금).\n * plan 미지정(레거시 단일 단가) 경로에는 무료 구간을 적용하지 않는다.\n */\n freeSecondsByPlan: {\n FREE: 10,\n PLUS: 20,\n PRO: 30,\n },\n} as const;\n\n/**\n * 다운로드 가능한 최소 길이(초).\n * 이보다 짧은 구간은 다운로드 요청 자체를 허용하지 않는다 (server·FE 공용 제약).\n */\nexport const MIN_DOWNLOAD_SECONDS = 10;\n\n/**\n * 다운로드 요청 길이가 허용되는지 검사한다.\n * `durationSeconds >= MIN_DOWNLOAD_SECONDS`일 때만 true.\n */\nexport function isDownloadDurationAllowed(durationSeconds: number): boolean {\n return (\n Number.isFinite(durationSeconds) && durationSeconds >= MIN_DOWNLOAD_SECONDS\n );\n}\n\nexport interface OriginalDownloadCostOptions {\n isFullVideo?: boolean;\n /**\n * 멤버십 plan. 지정 시 `perPlanKrwPerMinute[plan]`을 단가로,\n * `freeSecondsByPlan[plan]`을 무료 구간으로 적용한다.\n * 미지정(undefined) 시 레거시 단일 단가(`baseCostWon/baseDurationSeconds`) + 무료 구간 없음.\n */\n plan?: TicketCode;\n}\n\nexport function calculateOriginalDownloadCostWon(\n durationSeconds: number,\n options: OriginalDownloadCostOptions = {},\n): number {\n if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) return 0;\n\n const { plan, isFullVideo } = options;\n\n // plan 지정 시 앞 freeSeconds 구간은 과금에서 제외하고 초과분만 과금한다.\n // 레거시 경로(plan 미지정)는 무료 구간 0으로 기존 동작을 유지한다.\n const freeSeconds =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.freeSecondsByPlan[plan]\n : 0;\n const billableSeconds = Math.max(0, durationSeconds - freeSeconds);\n if (billableSeconds <= 0) return 0;\n\n const unitPricePerSecond =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.perPlanKrwPerMinute[plan] / 60\n : ORIGINAL_DOWNLOAD_COST_POLICY.baseCostWon /\n ORIGINAL_DOWNLOAD_COST_POLICY.baseDurationSeconds;\n\n const discountMultiplier = isFullVideo\n ? 1 - ORIGINAL_DOWNLOAD_COST_POLICY.fullVideoDiscountRate\n : 1;\n\n // 부동소수점 drift(예: 30.000000000000004) 때문에 1원 과다 청구되는 것을 막기 위해\n // 원 단위 미만 노이즈를 보정한 뒤 올림한다.\n const rawCost = billableSeconds * unitPricePerSecond * discountMultiplier;\n return Math.ceil(Number(rawCost.toFixed(6)));\n}\n","export const GYM_PARTNER_REGISTRATION_GOLD_POLICY = {\n goldPerDay: 350,\n timeZone: 'Asia/Seoul',\n} as const;\n\nexport interface GymPartnerRegistrationGoldInput {\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n startDate: string;\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n endDate: string;\n}\n\nexport interface GymPartnerRegistrationGoldResult {\n days: number;\n goldAmount: number;\n}\n\ninterface KstDateParts {\n year: number;\n month: number;\n day: number;\n}\n\nconst DATE_ONLY_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\n\nexport function calculateGymPartnerRegistrationGold(\n input: GymPartnerRegistrationGoldInput,\n): GymPartnerRegistrationGoldResult {\n const startDate = parseKstDateString(input.startDate, 'startDate');\n const endDate = parseKstDateString(input.endDate, 'endDate');\n\n const startDateKey = toUtcDateKey(startDate);\n const endDateKey = toUtcDateKey(endDate);\n\n if (endDateKey < startDateKey) {\n throw new RangeError('endDate must be on or after startDate');\n }\n\n const days = Math.floor((endDateKey - startDateKey) / MS_PER_DAY) + 1;\n\n return {\n days,\n goldAmount: days * GYM_PARTNER_REGISTRATION_GOLD_POLICY.goldPerDay,\n };\n}\n\nfunction parseKstDateString(\n input: string,\n fieldName: 'startDate' | 'endDate',\n): KstDateParts {\n const dateOnlyMatch = DATE_ONLY_PATTERN.exec(input);\n if (dateOnlyMatch === null) {\n throw new RangeError(`${fieldName} must be a YYYY-MM-DD date`);\n }\n\n const year = Number(dateOnlyMatch[1]);\n const month = Number(dateOnlyMatch[2]);\n const day = Number(dateOnlyMatch[3]);\n const utcDate = new Date(Date.UTC(year, month - 1, day));\n\n if (\n utcDate.getUTCFullYear() !== year ||\n utcDate.getUTCMonth() + 1 !== month ||\n utcDate.getUTCDate() !== day\n ) {\n throw new RangeError(`${fieldName} must be a valid date`);\n }\n\n return { year, month, day };\n}\n\nfunction toUtcDateKey(date: KstDateParts): number {\n return Date.UTC(date.year, date.month - 1, date.day);\n}\n"],"mappings":";AAEO,IAAM,gCAAgC;AAAA,EAC3C,qBAAqB;AAAA,EACrB,aAAa;AAAA,EACb,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvB,qBAAqB;AAAA,IACnB,MAAM;AAAA,IACN,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,mBAAmB;AAAA,IACjB,MAAM;AAAA,IACN,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AACF;AAMO,IAAM,uBAAuB;AAM7B,SAAS,0BAA0B,iBAAkC;AAC1E,SACE,OAAO,SAAS,eAAe,KAAK,mBAAmB;AAE3D;AAYO,SAAS,iCACd,iBACA,UAAuC,CAAC,GAChC;AACR,MAAI,CAAC,OAAO,SAAS,eAAe,KAAK,mBAAmB,EAAG,QAAO;AAEtE,QAAM,EAAE,MAAM,YAAY,IAAI;AAI9B,QAAM,cACJ,SAAS,SACL,8BAA8B,kBAAkB,IAAI,IACpD;AACN,QAAM,kBAAkB,KAAK,IAAI,GAAG,kBAAkB,WAAW;AACjE,MAAI,mBAAmB,EAAG,QAAO;AAEjC,QAAM,qBACJ,SAAS,SACL,8BAA8B,oBAAoB,IAAI,IAAI,KAC1D,8BAA8B,cAC9B,8BAA8B;AAEpC,QAAM,qBAAqB,cACvB,IAAI,8BAA8B,wBAClC;AAIJ,QAAM,UAAU,kBAAkB,qBAAqB;AACvD,SAAO,KAAK,KAAK,OAAO,QAAQ,QAAQ,CAAC,CAAC,CAAC;AAC7C;;;ACtFO,IAAM,uCAAuC;AAAA,EAClD,YAAY;AAAA,EACZ,UAAU;AACZ;AAwBA,IAAM,oBAAoB;AAC1B,IAAM,aAAa,KAAK,KAAK,KAAK;AAE3B,SAAS,oCACd,OACkC;AAClC,QAAM,YAAY,mBAAmB,MAAM,WAAW,WAAW;AACjE,QAAM,UAAU,mBAAmB,MAAM,SAAS,SAAS;AAE3D,QAAM,eAAe,aAAa,SAAS;AAC3C,QAAM,aAAa,aAAa,OAAO;AAEvC,MAAI,aAAa,cAAc;AAC7B,UAAM,IAAI,WAAW,uCAAuC;AAAA,EAC9D;AAEA,QAAM,OAAO,KAAK,OAAO,aAAa,gBAAgB,UAAU,IAAI;AAEpE,SAAO;AAAA,IACL;AAAA,IACA,YAAY,OAAO,qCAAqC;AAAA,EAC1D;AACF;AAEA,SAAS,mBACP,OACA,WACc;AACd,QAAM,gBAAgB,kBAAkB,KAAK,KAAK;AAClD,MAAI,kBAAkB,MAAM;AAC1B,UAAM,IAAI,WAAW,GAAG,SAAS,4BAA4B;AAAA,EAC/D;AAEA,QAAM,OAAO,OAAO,cAAc,CAAC,CAAC;AACpC,QAAM,QAAQ,OAAO,cAAc,CAAC,CAAC;AACrC,QAAM,MAAM,OAAO,cAAc,CAAC,CAAC;AACnC,QAAM,UAAU,IAAI,KAAK,KAAK,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC;AAEvD,MACE,QAAQ,eAAe,MAAM,QAC7B,QAAQ,YAAY,IAAI,MAAM,SAC9B,QAAQ,WAAW,MAAM,KACzB;AACA,UAAM,IAAI,WAAW,GAAG,SAAS,uBAAuB;AAAA,EAC1D;AAEA,SAAO,EAAE,MAAM,OAAO,IAAI;AAC5B;AAEA,SAAS,aAAa,MAA4B;AAChD,SAAO,KAAK,IAAI,KAAK,MAAM,KAAK,QAAQ,GAAG,KAAK,GAAG;AACrD;","names":[]}
1
+ {"version":3,"sources":["../src/libs/cost.ts","../src/libs/gym-registration-gold.ts"],"sourcesContent":["import type { TicketCode } from '../types/membership';\n\nexport const ORIGINAL_DOWNLOAD_COST_POLICY = {\n baseDurationSeconds: 600,\n baseCostWon: 2000,\n fullVideoDiscountRate: 0.1,\n /**\n * v14 신정책: 멤버십 plan별 분당 KRW 요율.\n * `calculateOriginalDownloadCostWon(duration, { plan })`로 호출 시 적용된다.\n * plan 미지정 시 `baseCostWon/baseDurationSeconds` 기반 단일 단가(레거시 200원/분)로 폴백 — 기존 호출자 호환.\n */\n perPlanKrwPerMinute: {\n FREE: 100,\n PLUS: 50,\n PRO: 25,\n },\n /**\n * 멤버십 plan별 무료 다운로드 구간(초).\n * 다운로드 길이 중 앞 `freeSecondsByPlan[plan]`초는 과금에서 제외하고,\n * 이를 초과한 구간에만 `perPlanKrwPerMinute[plan]` 요율을 적용한다 (초과분 과금).\n * plan 미지정(레거시 단일 단가) 경로에는 무료 구간을 적용하지 않는다.\n */\n freeSecondsByPlan: {\n FREE: 10,\n PLUS: 20,\n PRO: 30,\n },\n} as const;\n\n/**\n * 다운로드 가능한 최소 길이(초).\n * 이보다 짧은 구간은 다운로드 요청 자체를 허용하지 않는다 (server·FE 공용 제약).\n */\nexport const MIN_DOWNLOAD_SECONDS = 10;\n\n/**\n * 다운로드 요청 길이가 허용되는지 검사한다.\n * `durationSeconds >= MIN_DOWNLOAD_SECONDS`일 때만 true.\n */\nexport function isDownloadDurationAllowed(durationSeconds: number): boolean {\n return (\n Number.isFinite(durationSeconds) && durationSeconds >= MIN_DOWNLOAD_SECONDS\n );\n}\n\nexport interface OriginalDownloadCostOptions {\n isFullVideo?: boolean;\n /**\n * 멤버십 plan. 지정 시 `perPlanKrwPerMinute[plan]`을 단가로,\n * `freeSecondsByPlan[plan]`을 무료 구간으로 적용한다.\n * 미지정(undefined) 시 레거시 단일 단가(`baseCostWon/baseDurationSeconds`) + 무료 구간 없음.\n */\n plan?: TicketCode;\n}\n\nexport function calculateOriginalDownloadCostWon(\n durationSeconds: number,\n options: OriginalDownloadCostOptions = {},\n): number {\n if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) return 0;\n\n const { plan, isFullVideo } = options;\n\n // plan 지정 시 앞 freeSeconds 구간은 과금에서 제외하고 초과분만 과금한다.\n // 레거시 경로(plan 미지정)는 무료 구간 0으로 기존 동작을 유지한다.\n const freeSeconds =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.freeSecondsByPlan[plan]\n : 0;\n const billableSeconds = Math.max(0, durationSeconds - freeSeconds);\n if (billableSeconds <= 0) return 0;\n\n const unitPricePerSecond =\n plan !== undefined\n ? ORIGINAL_DOWNLOAD_COST_POLICY.perPlanKrwPerMinute[plan] / 60\n : ORIGINAL_DOWNLOAD_COST_POLICY.baseCostWon /\n ORIGINAL_DOWNLOAD_COST_POLICY.baseDurationSeconds;\n\n const discountMultiplier = isFullVideo\n ? 1 - ORIGINAL_DOWNLOAD_COST_POLICY.fullVideoDiscountRate\n : 1;\n\n // 부동소수점 drift(예: 30.000000000000004) 때문에 1원 과다 청구되는 것을 막기 위해\n // 원 단위 미만 노이즈를 보정한 뒤 올림한다.\n const rawCost = billableSeconds * unitPricePerSecond * discountMultiplier;\n return Math.ceil(Number(rawCost.toFixed(6)));\n}\n","export const GYM_PARTNER_REGISTRATION_GOLD_POLICY = {\n goldPerDay: 350,\n timeZone: 'Asia/Seoul',\n} as const;\n\nexport interface GymPartnerRegistrationGoldInput {\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n startDate: string;\n /**\n * KST calendar date in YYYY-MM-DD format.\n */\n endDate: string;\n}\n\nexport interface GymPartnerRegistrationGoldResult {\n days: number;\n goldAmount: number;\n}\n\nexport interface GymPartnerRegistrationGoldTotalItem\n extends GymPartnerRegistrationGoldInput,\n GymPartnerRegistrationGoldResult {}\n\nexport interface GymPartnerRegistrationGoldTotalResult {\n totalDays: number;\n totalGoldAmount: number;\n items: GymPartnerRegistrationGoldTotalItem[];\n}\n\ninterface KstDateParts {\n year: number;\n month: number;\n day: number;\n}\n\nconst DATE_ONLY_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\n\nexport function calculateGymPartnerRegistrationGold(\n input: GymPartnerRegistrationGoldInput,\n): GymPartnerRegistrationGoldResult {\n const startDate = parseKstDateString(input.startDate, 'startDate');\n const endDate = parseKstDateString(input.endDate, 'endDate');\n\n const startDateKey = toUtcDateKey(startDate);\n const endDateKey = toUtcDateKey(endDate);\n\n if (endDateKey < startDateKey) {\n throw new RangeError('endDate must be on or after startDate');\n }\n\n const days = Math.floor((endDateKey - startDateKey) / MS_PER_DAY) + 1;\n\n return {\n days,\n goldAmount: days * GYM_PARTNER_REGISTRATION_GOLD_POLICY.goldPerDay,\n };\n}\n\nexport function calculateGymPartnerRegistrationGoldTotal(\n periods: GymPartnerRegistrationGoldInput[],\n): GymPartnerRegistrationGoldTotalResult {\n const items = periods.map((period) => {\n const result = calculateGymPartnerRegistrationGold(period);\n\n return {\n ...period,\n ...result,\n };\n });\n\n return {\n totalDays: items.reduce((sum, item) => sum + item.days, 0),\n totalGoldAmount: items.reduce((sum, item) => sum + item.goldAmount, 0),\n items,\n };\n}\n\nfunction parseKstDateString(\n input: string,\n fieldName: 'startDate' | 'endDate',\n): KstDateParts {\n const dateOnlyMatch = DATE_ONLY_PATTERN.exec(input);\n if (dateOnlyMatch === null) {\n throw new RangeError(`${fieldName} must be a YYYY-MM-DD date`);\n }\n\n const year = Number(dateOnlyMatch[1]);\n const month = Number(dateOnlyMatch[2]);\n const day = Number(dateOnlyMatch[3]);\n const utcDate = new Date(Date.UTC(year, month - 1, day));\n\n if (\n utcDate.getUTCFullYear() !== year ||\n utcDate.getUTCMonth() + 1 !== month ||\n utcDate.getUTCDate() !== day\n ) {\n throw new RangeError(`${fieldName} must be a valid date`);\n }\n\n return { year, month, day };\n}\n\nfunction toUtcDateKey(date: KstDateParts): number {\n return Date.UTC(date.year, date.month - 1, date.day);\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spoclip-kit",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "type": "module",
5
5
  "description": "TypeScript utility library for internal use",
6
6
  "files": [