opensoma 0.6.0 → 0.7.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/package.json +1 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +6 -4
- package/dist/src/cli.js.map +1 -1
- package/dist/src/client.d.ts +4 -0
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +6 -0
- package/dist/src/client.js.map +1 -1
- package/dist/src/commands/index.d.ts +1 -0
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +1 -0
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/toz.d.ts +16 -0
- package/dist/src/commands/toz.d.ts.map +1 -0
- package/dist/src/commands/toz.js +488 -0
- package/dist/src/commands/toz.js.map +1 -0
- package/dist/src/credential-manager.d.ts +8 -2
- package/dist/src/credential-manager.d.ts.map +1 -1
- package/dist/src/credential-manager.js +25 -3
- package/dist/src/credential-manager.js.map +1 -1
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/session-recovery.js +2 -0
- package/dist/src/session-recovery.js.map +1 -1
- package/dist/src/toz-client.d.ts +89 -0
- package/dist/src/toz-client.d.ts.map +1 -0
- package/dist/src/toz-client.js +204 -0
- package/dist/src/toz-client.js.map +1 -0
- package/dist/src/toz-pending-store.d.ts +30 -0
- package/dist/src/toz-pending-store.d.ts.map +1 -0
- package/dist/src/toz-pending-store.js +36 -0
- package/dist/src/toz-pending-store.js.map +1 -0
- package/package.json +1 -1
- package/src/cli.ts +6 -3
- package/src/client.ts +10 -0
- package/src/commands/helpers.test.ts +4 -0
- package/src/commands/index.ts +1 -0
- package/src/commands/toz.test.ts +51 -0
- package/src/commands/toz.ts +607 -0
- package/src/credential-manager.test.ts +54 -0
- package/src/credential-manager.ts +26 -3
- package/src/index.ts +13 -0
- package/src/session-recovery.ts +2 -0
- package/src/toz-client.test.ts +243 -0
- package/src/toz-client.ts +311 -0
- package/src/toz-pending-store.test.ts +91 -0
- package/src/toz-pending-store.ts +62 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from 'bun:test'
|
|
2
|
+
import { mkdtempSync } from 'node:fs'
|
|
3
|
+
import { readdir, stat } from 'node:fs/promises'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
5
|
+
import { join } from 'node:path'
|
|
6
|
+
|
|
7
|
+
import { TozPendingStore, type TozPendingReservation } from './toz-pending-store'
|
|
8
|
+
|
|
9
|
+
let configDir: string
|
|
10
|
+
const cleanups: (() => void)[] = []
|
|
11
|
+
|
|
12
|
+
function freshStore(): TozPendingStore {
|
|
13
|
+
configDir = mkdtempSync(join(tmpdir(), 'toz-pending-'))
|
|
14
|
+
cleanups.push(() => {
|
|
15
|
+
require('node:fs').rmSync(configDir, { recursive: true, force: true })
|
|
16
|
+
})
|
|
17
|
+
return new TozPendingStore(configDir)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
while (cleanups.length > 0) cleanups.pop()?.()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const sample: TozPendingReservation = {
|
|
25
|
+
reservationId: 'abc-123',
|
|
26
|
+
cookies: { JSESSIONID: 'XYZ' },
|
|
27
|
+
branchName: '토즈타워점',
|
|
28
|
+
branchTel: '02-3454-0116',
|
|
29
|
+
boothGroupName: '2인부스 A타입',
|
|
30
|
+
isLargeBooth: false,
|
|
31
|
+
date: '2026-04-21',
|
|
32
|
+
startTime: '14:00',
|
|
33
|
+
endTime: '16:00',
|
|
34
|
+
durationMinutes: 120,
|
|
35
|
+
userCount: 2,
|
|
36
|
+
boothId: 740,
|
|
37
|
+
meetingId: 2305094,
|
|
38
|
+
email: 'me@gmail.com',
|
|
39
|
+
name: '홍길동',
|
|
40
|
+
phone: '010-1234-5678',
|
|
41
|
+
createdAt: '2026-04-17T18:42:13.291Z',
|
|
42
|
+
expiresAt: '2026-04-17T18:47:13.291Z',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe('TozPendingStore', () => {
|
|
46
|
+
test('returns null when no pending file exists', async () => {
|
|
47
|
+
const store = freshStore()
|
|
48
|
+
expect(await store.get()).toBeNull()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('round-trips a reservation through set/get', async () => {
|
|
52
|
+
const store = freshStore()
|
|
53
|
+
await store.set(sample)
|
|
54
|
+
expect(await store.get()).toEqual(sample)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('clear removes the file', async () => {
|
|
58
|
+
const store = freshStore()
|
|
59
|
+
await store.set(sample)
|
|
60
|
+
await store.clear()
|
|
61
|
+
expect(await store.get()).toBeNull()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('clear is no-op when file does not exist', async () => {
|
|
65
|
+
const store = freshStore()
|
|
66
|
+
await store.clear()
|
|
67
|
+
expect(await store.get()).toBeNull()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('overwrites an existing reservation', async () => {
|
|
71
|
+
const store = freshStore()
|
|
72
|
+
await store.set(sample)
|
|
73
|
+
const updated = { ...sample, reservationId: 'new-id' }
|
|
74
|
+
await store.set(updated)
|
|
75
|
+
expect(await store.get()).toEqual(updated)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('writes file with 0o600 permissions at creation (no widened window)', async () => {
|
|
79
|
+
const store = freshStore()
|
|
80
|
+
await store.set(sample)
|
|
81
|
+
const fileStat = await stat(join(configDir, 'toz-pending.json'))
|
|
82
|
+
expect(fileStat.mode & 0o777).toBe(0o600)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('leaves no temp file behind after set', async () => {
|
|
86
|
+
const store = freshStore()
|
|
87
|
+
await store.set(sample)
|
|
88
|
+
const entries = await readdir(configDir)
|
|
89
|
+
expect(entries).toEqual(['toz-pending.json'])
|
|
90
|
+
})
|
|
91
|
+
})
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { homedir } from 'node:os'
|
|
4
|
+
import { dirname, join } from 'node:path'
|
|
5
|
+
|
|
6
|
+
export interface TozPendingReservation {
|
|
7
|
+
reservationId: string
|
|
8
|
+
cookies: Record<string, string>
|
|
9
|
+
branchName: string
|
|
10
|
+
branchTel: string
|
|
11
|
+
boothGroupName: string
|
|
12
|
+
isLargeBooth: boolean
|
|
13
|
+
date: string
|
|
14
|
+
startTime: string
|
|
15
|
+
endTime: string
|
|
16
|
+
durationMinutes: number
|
|
17
|
+
userCount: number
|
|
18
|
+
boothId: number
|
|
19
|
+
meetingId?: number
|
|
20
|
+
newMeetingName?: string
|
|
21
|
+
email: string
|
|
22
|
+
memo?: string
|
|
23
|
+
name: string
|
|
24
|
+
phone: string
|
|
25
|
+
createdAt: string
|
|
26
|
+
expiresAt: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class TozPendingStore {
|
|
30
|
+
private readonly path: string
|
|
31
|
+
|
|
32
|
+
constructor(configDir?: string) {
|
|
33
|
+
const dir = configDir ?? join(homedir(), '.config', 'opensoma')
|
|
34
|
+
this.path = join(dir, 'toz-pending.json')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async get(): Promise<TozPendingReservation | null> {
|
|
38
|
+
if (!existsSync(this.path)) return null
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const raw = await readFile(this.path, 'utf8')
|
|
42
|
+
return JSON.parse(raw) as TozPendingReservation
|
|
43
|
+
} catch {
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async set(reservation: TozPendingReservation): Promise<void> {
|
|
49
|
+
const dir = dirname(this.path)
|
|
50
|
+
await mkdir(dir, { recursive: true })
|
|
51
|
+
// Write to a temp file with 0o600 from creation, then rename atomically.
|
|
52
|
+
// Avoids the default 0o644 window between writeFile() and chmod() that
|
|
53
|
+
// would briefly expose the cookie + phone number to other users on the system.
|
|
54
|
+
const tmp = `${this.path}.tmp-${process.pid}-${Date.now()}`
|
|
55
|
+
await writeFile(tmp, JSON.stringify(reservation, null, 2), { mode: 0o600 })
|
|
56
|
+
await rename(tmp, this.path)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async clear(): Promise<void> {
|
|
60
|
+
await rm(this.path, { force: true })
|
|
61
|
+
}
|
|
62
|
+
}
|