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.
Files changed (49) hide show
  1. package/dist/package.json +1 -1
  2. package/dist/src/cli.d.ts.map +1 -1
  3. package/dist/src/cli.js +6 -4
  4. package/dist/src/cli.js.map +1 -1
  5. package/dist/src/client.d.ts +4 -0
  6. package/dist/src/client.d.ts.map +1 -1
  7. package/dist/src/client.js +6 -0
  8. package/dist/src/client.js.map +1 -1
  9. package/dist/src/commands/index.d.ts +1 -0
  10. package/dist/src/commands/index.d.ts.map +1 -1
  11. package/dist/src/commands/index.js +1 -0
  12. package/dist/src/commands/index.js.map +1 -1
  13. package/dist/src/commands/toz.d.ts +16 -0
  14. package/dist/src/commands/toz.d.ts.map +1 -0
  15. package/dist/src/commands/toz.js +488 -0
  16. package/dist/src/commands/toz.js.map +1 -0
  17. package/dist/src/credential-manager.d.ts +8 -2
  18. package/dist/src/credential-manager.d.ts.map +1 -1
  19. package/dist/src/credential-manager.js +25 -3
  20. package/dist/src/credential-manager.js.map +1 -1
  21. package/dist/src/index.d.ts +4 -0
  22. package/dist/src/index.d.ts.map +1 -1
  23. package/dist/src/index.js +2 -0
  24. package/dist/src/index.js.map +1 -1
  25. package/dist/src/session-recovery.js +2 -0
  26. package/dist/src/session-recovery.js.map +1 -1
  27. package/dist/src/toz-client.d.ts +89 -0
  28. package/dist/src/toz-client.d.ts.map +1 -0
  29. package/dist/src/toz-client.js +204 -0
  30. package/dist/src/toz-client.js.map +1 -0
  31. package/dist/src/toz-pending-store.d.ts +30 -0
  32. package/dist/src/toz-pending-store.d.ts.map +1 -0
  33. package/dist/src/toz-pending-store.js +36 -0
  34. package/dist/src/toz-pending-store.js.map +1 -0
  35. package/package.json +1 -1
  36. package/src/cli.ts +6 -3
  37. package/src/client.ts +10 -0
  38. package/src/commands/helpers.test.ts +4 -0
  39. package/src/commands/index.ts +1 -0
  40. package/src/commands/toz.test.ts +51 -0
  41. package/src/commands/toz.ts +607 -0
  42. package/src/credential-manager.test.ts +54 -0
  43. package/src/credential-manager.ts +26 -3
  44. package/src/index.ts +13 -0
  45. package/src/session-recovery.ts +2 -0
  46. package/src/toz-client.test.ts +243 -0
  47. package/src/toz-client.ts +311 -0
  48. package/src/toz-pending-store.test.ts +91 -0
  49. 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
+ }