@ahhaohho/event-publisher 0.1.1 → 0.2.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/README.md CHANGED
@@ -134,6 +134,7 @@ payload에는 id와 작은 사실값만 싣는다. 닉네임·댓글 본문·썸
134
134
  | `ai.comment.created` | ai-chatbot | comment.service.ts | 봇 계정 | 게시자 |
135
135
  | `app.activity.challenge_viewed` | challenge | 챌린지 상세 API | 본인 | 본인 |
136
136
  | `app.activity.gallery_post_viewed` | gallery | 갤러리 상세 API | 본인 | 본인 |
137
+ | `app.activity.session_active` | gallery, challenge | 인증된 앱 요청 (사용자당 ~10분 throttle, 박스별 중복 가능 → 소비자 debounce) | 본인 | 본인 |
137
138
  | `app.activity.post_step_viewed` | (POST /activity 수신 서버) | 앱 신호 | 본인 | 본인 |
138
139
  | `campaign.schedule.fired` | notification (Scheduler) | — | null | (비움) |
139
140
 
@@ -164,7 +165,7 @@ npm run build # dist/
164
165
  - **CI** (`.github/workflows/ci.yml`): 모든 PR과 main push에서 Node 20·22로 typecheck → test → build → 패키지 파일 구성 검사.
165
166
  - **배포** (`.github/workflows/publish.yml`): **`package.json` version을 올린 PR을 main에 머지하면 배포된다.**
166
167
  1. main push 때 그 version이 npm에 없으면 publish한다. 이미 있으면 아무 것도 하지 않는다(일반 머지는 no-op).
167
- 2. 익명으로 레지스트리를 조회해 200인지 확인한다. 전파 지연을 고려해 10초 간격으로 6번까지 재시도하고, 그래도 아니면 실패 처리(private으로 나간 경우).
168
+ 2. 익명으로 레지스트리를 조회해 200인지 확인한다. 전파 지연을 고려해 15초 간격으로 최대 5분 재시도하고, 그래도 아니면 실패 처리(private으로 나간 경우).
168
169
  3. `v{version}` 태그가 없으면 만든다. publish 여부와 무관하게 매번 확인하므로, 태그 push만 실패했거나 로컬에서 수동 publish한 버전도 다음 main push에서 태그가 생긴다.
169
170
 
170
171
  > 루트 CLAUDE.md의 "SDK 배포 워크플로우"(머지 후 `npm version` → 수동 `npm publish`)는 이 레포에 적용하지 않는다. main 직접 커밋·수동 publish 대신 버전 범프 PR을 머지한다. 수동 publish를 했더라도 워크플로는 skip + 태그만 만들므로 충돌은 없다.
@@ -5,7 +5,9 @@ import { z } from 'zod';
5
5
  * - 서버가 API 호출에서 직접 아는 신호는 해당 서버가 발행한다 (challenge_viewed, gallery_post_viewed).
6
6
  * - 서버가 모르는 화면 단계 신호(post_step_viewed)만 앱이 `POST /activity`로 보내고, 받는 서버가 발행한다.
7
7
  * - userId는 항상 JWT 검증값이다. 요청 body/query의 userId를 쓰지 않는다.
8
- * - 단순 접속(lastActiveAt)은 요청마다 이벤트를 만들지 않고 Redis → DB로 따로 반영한다.
8
+ * - 단순 접속(lastActiveAt)은 session_active 이벤트로 반영한다. prod는 박스별 Redis라 서버 간에 접속 상태를
9
+ * 공유할 수 없어서, 발행 서버가 사용자당 약 10분 간격으로 throttle해 발행한다.
10
+ * throttle이 박스별이므로 같은 사용자의 이벤트가 박스 수만큼 중복될 수 있고, 소비자가 debounce한다.
9
11
  */
10
12
  export declare const appActivityEvents: {
11
13
  'app.activity.challenge_viewed': import("../envelope").EventDefinition<z.ZodObject<{
@@ -17,6 +19,9 @@ export declare const appActivityEvents: {
17
19
  galleryId: z.ZodString;
18
20
  communityId: z.ZodOptional<z.ZodString>;
19
21
  }, z.core.$strip>>;
22
+ 'app.activity.session_active': import("../envelope").EventDefinition<z.ZodObject<{
23
+ userId: z.ZodString;
24
+ }, z.core.$strip>>;
20
25
  'app.activity.post_step_viewed': import("../envelope").EventDefinition<z.ZodObject<{
21
26
  userId: z.ZodString;
22
27
  stepId: z.ZodString;
@@ -9,7 +9,9 @@ const envelope_1 = require("../envelope");
9
9
  * - 서버가 API 호출에서 직접 아는 신호는 해당 서버가 발행한다 (challenge_viewed, gallery_post_viewed).
10
10
  * - 서버가 모르는 화면 단계 신호(post_step_viewed)만 앱이 `POST /activity`로 보내고, 받는 서버가 발행한다.
11
11
  * - userId는 항상 JWT 검증값이다. 요청 body/query의 userId를 쓰지 않는다.
12
- * - 단순 접속(lastActiveAt)은 요청마다 이벤트를 만들지 않고 Redis → DB로 따로 반영한다.
12
+ * - 단순 접속(lastActiveAt)은 session_active 이벤트로 반영한다. prod는 박스별 Redis라 서버 간에 접속 상태를
13
+ * 공유할 수 없어서, 발행 서버가 사용자당 약 10분 간격으로 throttle해 발행한다.
14
+ * throttle이 박스별이므로 같은 사용자의 이벤트가 박스 수만큼 중복될 수 있고, 소비자가 debounce한다.
13
15
  */
14
16
  exports.appActivityEvents = {
15
17
  'app.activity.challenge_viewed': (0, envelope_1.defineEvent)({
@@ -35,6 +37,16 @@ exports.appActivityEvents = {
35
37
  actor: (p) => p.userId,
36
38
  subjects: (p) => [p.userId],
37
39
  }),
40
+ 'app.activity.session_active': (0, envelope_1.defineEvent)({
41
+ version: 1,
42
+ publishers: ['ao-gallery-server', 'ao-challenge-server'],
43
+ description: '인증된 앱 요청 = 접속 신호 (S3 비활동 트리거 lastActiveAt). 발행 서버가 사용자당 약 10분 간격으로 throttle',
44
+ schema: zod_1.z.object({
45
+ userId: envelope_1.ids.userId,
46
+ }),
47
+ actor: (p) => p.userId,
48
+ subjects: (p) => [p.userId],
49
+ }),
38
50
  'app.activity.post_step_viewed': (0, envelope_1.defineEvent)({
39
51
  version: 1,
40
52
  // POST /activity를 받을 서버는 S3 카드(qSPjswwl)에서 확정 — 우선 notification-server로 둔다.
@@ -26,6 +26,9 @@ export declare const catalog: {
26
26
  galleryId: z.ZodString;
27
27
  communityId: z.ZodOptional<z.ZodString>;
28
28
  }, z.core.$strip>>;
29
+ readonly 'app.activity.session_active': import("../envelope").EventDefinition<z.ZodObject<{
30
+ userId: z.ZodString;
31
+ }, z.core.$strip>>;
29
32
  readonly 'app.activity.post_step_viewed': import("../envelope").EventDefinition<z.ZodObject<{
30
33
  userId: z.ZodString;
31
34
  stepId: z.ZodString;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ahhaohho/event-publisher",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "AhhaOhho 도메인 이벤트 발행 SDK — EventBridge envelope + Zod 이벤트 카탈로그",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",