@things-factory/webhook 10.1.206 → 10.1.212

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.
@@ -6,7 +6,7 @@ import type { DeliveryOutcome } from './delivery.js';
6
6
  * ## 왜 떼어 냈나 (2026-09-18)
7
7
  *
8
8
  * 이 판단이 `dispatchPending` 의 `switch` 안에 살았다. 그래서 **어느 시험도 못 지났다** — 그 갈래에
9
- * 닿으려면 EntityManager 와 503 을 주는 받는 쪽이 있어야 하고, 이 꾸러미의 시험은 가볍게 import 하는
9
+ * 닿으려면 EntityManager 와 503 을 주는 받는 쪽이 있어야 하고, 이 패키지의 시험은 가볍게 import 하는
10
10
  * 것을 규율로 삼는다. 배압 갈래의 주석이 그것을 스스로 적어 두었다(「⚠ 이 줄을 도는 시험이 없다 …
11
11
  * outcome→동작 판단을 떼어 내는 사람이 그때 못 박아라」). 이 파일이 그 자리다.
12
12
  *
@@ -1 +1 @@
1
- {"version":3,"file":"row-effect.js","sourceRoot":"","sources":["../../../server/service/outbox/row-effect.ts"],"names":[],"mappings":";;AAqDA,kCAyDC;AA1DD,uDAAuD;AACvD,SAAgB,WAAW,CAAC,OAAwB,EAAE,KAAa;IACjE,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,UAAU,CAAC;QAChB,KAAK,SAAS,CAAC;QACf,KAAK,KAAK;YACR,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,CAAA;QAEnD,KAAK,MAAM;YACT,OAAO;gBACL,IAAI,EAAE,MAAM;gBACZ,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,eAAe,EAAE,IAAI;gBACrB,GAAG,EAAE;oBACH,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE,aAAa,OAAO,CAAC,MAAM,QAAQ,KAAK,uBAAuB;iBACtE;aACF,CAAA;QAEH,KAAK,cAAc;YACjB,OAAO;gBACL,IAAI,EAAE,MAAM;gBACZ,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,eAAe,EAAE,KAAK;gBACtB,GAAG,EAAE;oBACH,KAAK,EAAE,MAAM;oBACb,IAAI,EACF,2CAA2C,KAAK,iBAAiB;wBACjE,GAAG,OAAO,CAAC,YAAY,sDAAsD;iBAChF;aACF,CAAA;QAEH,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,gGAAgG;YAChG,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;YAEzD,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,6BAA6B,IAAI,EAAE;gBAC3C,eAAe,EAAE,IAAI;gBACrB,GAAG,EAAE;oBACH,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE,sDAAsD,IAAI,4CAA4C;iBAC7G;aACF,CAAA;QACH,CAAC;QAED;;;WAGG;QACH;YACE,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,MAAM,EAAG,OAA+B,CAAC,MAAM,IAAI,yBAAyB,OAAO,CAAC,IAAI,mBAAmB;gBAC3G,eAAe,EAAE,IAAI;aACtB,CAAA;IACL,CAAC;AACH,CAAC","sourcesContent":["import type { DeliveryOutcome } from './delivery.js'\n\n/**\n * 받는 쪽의 답이 **우리 행에 무슨 일을 하나** — 저장소를 모르는 판단.\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ## 왜 떼어 냈나 (2026-09-18)\n *\n * 이 판단이 `dispatchPending` 의 `switch` 안에 살았다. 그래서 **어느 시험도 못 지났다** — 그 갈래에\n * 닿으려면 EntityManager 와 503 을 주는 받는 쪽이 있어야 하고, 이 꾸러미의 시험은 가볍게 import 하는\n * 것을 규율로 삼는다. 배압 갈래의 주석이 그것을 스스로 적어 두었다(「⚠ 이 줄을 도는 시험이 없다 …\n * outcome→동작 판단을 떼어 내는 사람이 그때 못 박아라」). 이 파일이 그 자리다.\n *\n * ## 배압만 시도로 세지 않는다\n *\n * ```\n * accepted · partial · gap 범위를 갈라 표시한다 — 어디까지 닿았나는 부르는 쪽이 안다\n * stop 다시 보내도 같다 → 실패로 두고 흐름에서 뺀다\n * backpressure 「나중에 오라」 → 아무것도 안 바꾼다. 시도로 세지 않는다\n * unauthorized · retry · 그 밖 시도 횟수와 이유만 남기고 상태는 둔다 — 다음에 다시 보낸다\n * ```\n *\n * 배압을 시도로 세면 **기다려 달라는 받는 쪽에게 재시도 예산을 쓴다.** 그래서 안 센다. 대가는\n * `attempts: 0` 이 증거가 못 되는 것이다 — 백 번 붙들려도 행은 `attempts 0` 이고 `lastError` 가\n * 없다. 그 사실을 아는 유일한 자리가 로그 한 줄이라, **세지 않고 되풀이된다**: 한 줄은 정상이고\n * 벽처럼 쌓이면 영영 못 가는 보내는 쪽이다.\n *\n * ## `stop` 은 상태까지 바꾼다\n *\n * 시도 횟수만 남기고 상태를 안 바꿔 두 가지가 났다(2026-09-06). 매 주기 다시 보내 seq 1~20 이\n * 시도 382회가 됐고, 나중 배치에 딸려 들어가 `SENT` 로 찍혔다 — **받는 쪽에는 없는데 우리 표는 다\n * 보냈다고 말한다.** 그래서 이 갈래는 실패로 두고 흐름에서 뺀다. 봉투는 그대로 남으므로 backfill 은\n * `since: 0` 으로 다시 물어 가면 된다.\n * ═══════════════════════════════════════════════════════════════════════════\n */\n\nexport type RowEffect =\n /** 어디까지 닿았는지로 갈라 표시한다. 범위는 `outcome` 이 들고 있고 부르는 쪽이 읽는다. */\n | { rows: 'settle'; countsAsAttempt: false }\n /** 상태를 실패로 바꾸고 시도도 남긴다 — 다시 보내지 않는다. */\n | { rows: 'fail'; reason: string; countsAsAttempt: true; say: SayIt }\n /** 시도 횟수와 이유만 남긴다. 상태는 그대로 — 다음 주기에 다시 보낸다. */\n | { rows: 'attempt'; reason: string; countsAsAttempt: true; say?: SayIt }\n /** 아무것도 안 바꾼다. **시도로 세지 않는다.** */\n | { rows: 'hold'; retryAfterMs: number; countsAsAttempt: false; say: SayIt }\n\n/** 남길 말 — 수준까지 판단에 든다. 부르는 쪽은 고르지 않는다. */\nexport interface SayIt {\n level: 'info' | 'warn' | 'error'\n what: string\n}\n\n/** 이 답이 우리 행에 하는 일. `count` 는 이 배치의 봉투 수이고 말에만 쓰인다. */\nexport function rowEffectOf(outcome: DeliveryOutcome, count: number): RowEffect {\n switch (outcome.kind) {\n case 'accepted':\n case 'partial':\n case 'gap':\n return { rows: 'settle', countsAsAttempt: false }\n\n case 'stop':\n return {\n rows: 'fail',\n reason: outcome.reason,\n countsAsAttempt: true,\n say: {\n level: 'error',\n what: `[webhook] ${outcome.reason} — 이 ${count}건을 실패로 두고 다시 보내지 않습니다`\n }\n }\n\n case 'backpressure':\n return {\n rows: 'hold',\n retryAfterMs: outcome.retryAfterMs,\n countsAsAttempt: false,\n say: {\n level: 'warn',\n what:\n `[webhook] the receiver asked us to hold ${count} record(s) for ` +\n `${outcome.retryAfterMs}ms — held, not failed, and not counted as an attempt`\n }\n }\n\n case 'unauthorized': {\n /* What the receiver said decides where to look: a mismatch is the key, `expired` is a clock. */\n const said = outcome.reason ? ` — ${outcome.reason}` : ''\n\n return {\n rows: 'attempt',\n reason: `signature was not accepted${said}`,\n countsAsAttempt: true,\n say: {\n level: 'error',\n what: `[webhook] the receiver did not accept our signature${said} — check the shared secret before retrying`\n }\n }\n }\n\n /*\n * `retry` 와 앞으로 늘어날 갈래. **이유를 지어내지 않는다** — 이유 칸이 없는 답이 오면 그 사실을\n * 그대로 적는다. 「알 수 없음」을 로그가 말하는 것과, 없는 이유를 우리가 채워 넣는 것은 다르다.\n */\n default:\n return {\n rows: 'attempt',\n reason: (outcome as { reason?: string }).reason ?? `the receiver answered ${outcome.kind} without a reason`,\n countsAsAttempt: true\n }\n }\n}\n"]}
1
+ {"version":3,"file":"row-effect.js","sourceRoot":"","sources":["../../../server/service/outbox/row-effect.ts"],"names":[],"mappings":";;AAqDA,kCAyDC;AA1DD,uDAAuD;AACvD,SAAgB,WAAW,CAAC,OAAwB,EAAE,KAAa;IACjE,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,UAAU,CAAC;QAChB,KAAK,SAAS,CAAC;QACf,KAAK,KAAK;YACR,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,CAAA;QAEnD,KAAK,MAAM;YACT,OAAO;gBACL,IAAI,EAAE,MAAM;gBACZ,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,eAAe,EAAE,IAAI;gBACrB,GAAG,EAAE;oBACH,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE,aAAa,OAAO,CAAC,MAAM,QAAQ,KAAK,uBAAuB;iBACtE;aACF,CAAA;QAEH,KAAK,cAAc;YACjB,OAAO;gBACL,IAAI,EAAE,MAAM;gBACZ,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,eAAe,EAAE,KAAK;gBACtB,GAAG,EAAE;oBACH,KAAK,EAAE,MAAM;oBACb,IAAI,EACF,2CAA2C,KAAK,iBAAiB;wBACjE,GAAG,OAAO,CAAC,YAAY,sDAAsD;iBAChF;aACF,CAAA;QAEH,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,gGAAgG;YAChG,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;YAEzD,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,MAAM,EAAE,6BAA6B,IAAI,EAAE;gBAC3C,eAAe,EAAE,IAAI;gBACrB,GAAG,EAAE;oBACH,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE,sDAAsD,IAAI,4CAA4C;iBAC7G;aACF,CAAA;QACH,CAAC;QAED;;;WAGG;QACH;YACE,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,MAAM,EAAG,OAA+B,CAAC,MAAM,IAAI,yBAAyB,OAAO,CAAC,IAAI,mBAAmB;gBAC3G,eAAe,EAAE,IAAI;aACtB,CAAA;IACL,CAAC;AACH,CAAC","sourcesContent":["import type { DeliveryOutcome } from './delivery.js'\n\n/**\n * 받는 쪽의 답이 **우리 행에 무슨 일을 하나** — 저장소를 모르는 판단.\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ## 왜 떼어 냈나 (2026-09-18)\n *\n * 이 판단이 `dispatchPending` 의 `switch` 안에 살았다. 그래서 **어느 시험도 못 지났다** — 그 갈래에\n * 닿으려면 EntityManager 와 503 을 주는 받는 쪽이 있어야 하고, 이 패키지의 시험은 가볍게 import 하는\n * 것을 규율로 삼는다. 배압 갈래의 주석이 그것을 스스로 적어 두었다(「⚠ 이 줄을 도는 시험이 없다 …\n * outcome→동작 판단을 떼어 내는 사람이 그때 못 박아라」). 이 파일이 그 자리다.\n *\n * ## 배압만 시도로 세지 않는다\n *\n * ```\n * accepted · partial · gap 범위를 갈라 표시한다 — 어디까지 닿았나는 부르는 쪽이 안다\n * stop 다시 보내도 같다 → 실패로 두고 흐름에서 뺀다\n * backpressure 「나중에 오라」 → 아무것도 안 바꾼다. 시도로 세지 않는다\n * unauthorized · retry · 그 밖 시도 횟수와 이유만 남기고 상태는 둔다 — 다음에 다시 보낸다\n * ```\n *\n * 배압을 시도로 세면 **기다려 달라는 받는 쪽에게 재시도 예산을 쓴다.** 그래서 안 센다. 대가는\n * `attempts: 0` 이 증거가 못 되는 것이다 — 백 번 붙들려도 행은 `attempts 0` 이고 `lastError` 가\n * 없다. 그 사실을 아는 유일한 자리가 로그 한 줄이라, **세지 않고 되풀이된다**: 한 줄은 정상이고\n * 벽처럼 쌓이면 영영 못 가는 보내는 쪽이다.\n *\n * ## `stop` 은 상태까지 바꾼다\n *\n * 시도 횟수만 남기고 상태를 안 바꿔 두 가지가 났다(2026-09-06). 매 주기 다시 보내 seq 1~20 이\n * 시도 382회가 됐고, 나중 배치에 딸려 들어가 `SENT` 로 찍혔다 — **받는 쪽에는 없는데 우리 표는 다\n * 보냈다고 말한다.** 그래서 이 갈래는 실패로 두고 흐름에서 뺀다. 봉투는 그대로 남으므로 backfill 은\n * `since: 0` 으로 다시 물어 가면 된다.\n * ═══════════════════════════════════════════════════════════════════════════\n */\n\nexport type RowEffect =\n /** 어디까지 닿았는지로 갈라 표시한다. 범위는 `outcome` 이 들고 있고 부르는 쪽이 읽는다. */\n | { rows: 'settle'; countsAsAttempt: false }\n /** 상태를 실패로 바꾸고 시도도 남긴다 — 다시 보내지 않는다. */\n | { rows: 'fail'; reason: string; countsAsAttempt: true; say: SayIt }\n /** 시도 횟수와 이유만 남긴다. 상태는 그대로 — 다음 주기에 다시 보낸다. */\n | { rows: 'attempt'; reason: string; countsAsAttempt: true; say?: SayIt }\n /** 아무것도 안 바꾼다. **시도로 세지 않는다.** */\n | { rows: 'hold'; retryAfterMs: number; countsAsAttempt: false; say: SayIt }\n\n/** 남길 말 — 수준까지 판단에 든다. 부르는 쪽은 고르지 않는다. */\nexport interface SayIt {\n level: 'info' | 'warn' | 'error'\n what: string\n}\n\n/** 이 답이 우리 행에 하는 일. `count` 는 이 배치의 봉투 수이고 말에만 쓰인다. */\nexport function rowEffectOf(outcome: DeliveryOutcome, count: number): RowEffect {\n switch (outcome.kind) {\n case 'accepted':\n case 'partial':\n case 'gap':\n return { rows: 'settle', countsAsAttempt: false }\n\n case 'stop':\n return {\n rows: 'fail',\n reason: outcome.reason,\n countsAsAttempt: true,\n say: {\n level: 'error',\n what: `[webhook] ${outcome.reason} — 이 ${count}건을 실패로 두고 다시 보내지 않습니다`\n }\n }\n\n case 'backpressure':\n return {\n rows: 'hold',\n retryAfterMs: outcome.retryAfterMs,\n countsAsAttempt: false,\n say: {\n level: 'warn',\n what:\n `[webhook] the receiver asked us to hold ${count} record(s) for ` +\n `${outcome.retryAfterMs}ms — held, not failed, and not counted as an attempt`\n }\n }\n\n case 'unauthorized': {\n /* What the receiver said decides where to look: a mismatch is the key, `expired` is a clock. */\n const said = outcome.reason ? ` — ${outcome.reason}` : ''\n\n return {\n rows: 'attempt',\n reason: `signature was not accepted${said}`,\n countsAsAttempt: true,\n say: {\n level: 'error',\n what: `[webhook] the receiver did not accept our signature${said} — check the shared secret before retrying`\n }\n }\n }\n\n /*\n * `retry` 와 앞으로 늘어날 갈래. **이유를 지어내지 않는다** — 이유 칸이 없는 답이 오면 그 사실을\n * 그대로 적는다. 「알 수 없음」을 로그가 말하는 것과, 없는 이유를 우리가 채워 넣는 것은 다르다.\n */\n default:\n return {\n rows: 'attempt',\n reason: (outcome as { reason?: string }).reason ?? `the receiver answered ${outcome.kind} without a reason`,\n countsAsAttempt: true\n }\n }\n}\n"]}