ugly-app 0.1.584 → 0.1.585

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.
@@ -1,2 +1,2 @@
1
- export declare const CLI_VERSION = "0.1.584";
1
+ export declare const CLI_VERSION = "0.1.585";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by prebuild — do not edit manually
2
- export const CLI_VERSION = "0.1.584";
2
+ export const CLI_VERSION = "0.1.585";
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LoginPopup.d.ts","sourceRoot":"","sources":["../../src/client/LoginPopup.tsx"],"names":[],"mappings":"AAQA,UAAU,eAAe;IACvB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,sFAAsF;IACtF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,UAAU,CAAC,EAAE,SAAS,EAAE,MAAc,EAAE,EAAE,eAAe,+BAiNxE"}
1
+ {"version":3,"file":"LoginPopup.d.ts","sourceRoot":"","sources":["../../src/client/LoginPopup.tsx"],"names":[],"mappings":"AAQA,UAAU,eAAe;IACvB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,sFAAsF;IACtF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,UAAU,CAAC,EAAE,SAAS,EAAE,MAAc,EAAE,EAAE,eAAe,+BA0NxE"}
@@ -1,11 +1,16 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useState } from 'react';
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
3
  const UGLY_BOT_URL = typeof window !== 'undefined'
4
4
  ? (window.__UGLY_BOT_URL__ ??
5
5
  'https://ugly.bot')
6
6
  : 'https://ugly.bot';
7
7
  export function LoginPopup({ onSuccess, inline = false }) {
8
8
  const [status, setStatus] = useState('idle');
9
+ // An OAuth code is single-use — redeem it (popup OR redirect path) exactly
10
+ // once, even if an effect re-runs (e.g. an unmemoized `onSuccess`) before the
11
+ // async verify resolves. A second redemption of the consumed code makes
12
+ // ugly.bot return "Code expired or not found" and login fails.
13
+ const redeemedRef = useRef(false);
9
14
  useEffect(() => {
10
15
  const handler = async (event) => {
11
16
  if (event.origin !== UGLY_BOT_URL)
@@ -13,6 +18,9 @@ export function LoginPopup({ onSuccess, inline = false }) {
13
18
  const data = event.data;
14
19
  if (data?.type !== 'ugly-bot-oauth' || !data.code)
15
20
  return;
21
+ if (redeemedRef.current)
22
+ return;
23
+ redeemedRef.current = true;
16
24
  setStatus('waiting');
17
25
  try {
18
26
  const res = await fetch('/auth/verify', {
@@ -38,10 +46,13 @@ export function LoginPopup({ onSuccess, inline = false }) {
38
46
  // way the popup path does, then strip the param so a refresh can't replay a
39
47
  // consumed (deleted) code.
40
48
  useEffect(() => {
49
+ if (redeemedRef.current)
50
+ return;
41
51
  const params = new URLSearchParams(window.location.search);
42
52
  const code = params.get('ugly_oauth_code');
43
53
  if (!code)
44
54
  return;
55
+ redeemedRef.current = true;
45
56
  setStatus('waiting');
46
57
  (async () => {
47
58
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"LoginPopup.js","sourceRoot":"","sources":["../../src/client/LoginPopup.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LoginPopup.js","sourceRoot":"","sources":["../../src/client/LoginPopup.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjE,MAAM,YAAY,GAChB,OAAO,MAAM,KAAK,WAAW;IAC3B,CAAC,CAAC,CAAE,MAA4C,CAAC,gBAAgB;QAC7D,kBAAkB,CAAC;IACvB,CAAC,CAAC,kBAAkB,CAAC;AAQzB,MAAM,UAAU,UAAU,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,EAAmB;IACvE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAA+B,MAAM,CAAC,CAAC;IAC3E,2EAA2E;IAC3E,8EAA8E;IAC9E,wEAAwE;IACxE,+DAA+D;IAC/D,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAElC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,OAAO,GAAG,KAAK,EAAE,KAAmB,EAAE,EAAE;YAC5C,IAAI,KAAK,CAAC,MAAM,KAAK,YAAY;gBAAE,OAAO;YAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,IAA+C,CAAC;YACnE,IAAI,IAAI,EAAE,IAAI,KAAK,gBAAgB,IAAI,CAAC,IAAI,CAAC,IAAI;gBAAE,OAAO;YAC1D,IAAI,WAAW,CAAC,OAAO;gBAAE,OAAO;YAChC,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;YAE3B,SAAS,CAAC,SAAS,CAAC,CAAC;YACrB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,cAAc,EAAE;oBACtC,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;oBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;iBAC1C,CAAC,CAAC;gBACH,IAAI,CAAC,GAAG,CAAC,EAAE;oBAAE,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC;gBAC5C,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAsB,CAAC;gBAC1D,SAAS,CAAC,KAAK,CAAC,CAAC;YACnB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,CAAC,OAAO,CAAC,CAAC;YACrB,CAAC;QACH,CAAC,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC5C,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC9D,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,4EAA4E;IAC5E,6EAA6E;IAC7E,2EAA2E;IAC3E,4EAA4E;IAC5E,2BAA2B;IAC3B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,WAAW,CAAC,OAAO;YAAE,OAAO;QAChC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC3D,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;QAC3B,SAAS,CAAC,SAAS,CAAC,CAAC;QACrB,CAAC,KAAK,IAAI,EAAE;YACV,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,cAAc,EAAE;oBACtC,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;oBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC;iBAC/B,CAAC,CAAC;gBACH,IAAI,CAAC,GAAG,CAAC,EAAE;oBAAE,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC;gBAC5C,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAsB,CAAC;gBAC1D,iEAAiE;gBACjE,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;gBACjC,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;gBAC7B,MAAM,CAAC,OAAO,CAAC,YAAY,CACzB,EAAE,EACF,EAAE,EACF,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CACvE,CAAC;gBACF,SAAS,CAAC,KAAK,CAAC,CAAC;YACnB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,CAAC,OAAO,CAAC,CAAC;YACrB,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;QACjC,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC1D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CACvB,GAAG,YAAY,iBAAiB,MAAM,EAAE,EACxC,gBAAgB,EAChB,uCAAuC,CACxC,CAAC;QACF,SAAS,CAAC,SAAS,CAAC,CAAC;QAErB,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;gBAChC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;oBACjB,aAAa,CAAC,QAAQ,CAAC,CAAC;oBACxB,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC5D,CAAC;YACH,CAAC,EAAE,GAAG,CAAC,CAAC;QACV,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,SAAS,GAAG,MAAM,KAAK,SAAS,CAAC;IAEvC,OAAO,CACL,eACE,KAAK,EAAE;YACL,OAAO,EAAE,MAAM;YACf,aAAa,EAAE,QAAQ;YACvB,UAAU,EAAE,QAAQ;YACpB,cAAc,EAAE,QAAQ;YACxB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;YAC/D,GAAG,EAAE,EAAE;SACR,aAED,0BAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqFP,GAAS,EACT,MAAM,KAAK,OAAO,IAAI,CACrB,YACE,KAAK,EAAE;oBACL,MAAM,EAAE,CAAC;oBACT,KAAK,EAAE,SAAS;oBAChB,QAAQ,EAAE,EAAE;oBACZ,UAAU,EAAE,GAAG;iBAChB,gDAGC,CACL,EACD,iBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,gBAAgB,EAC1B,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,SAAS,eACR,SAAS,YAEnB,SAAS,CAAC,CAAC,CAAC,CACX,8BACE,eAAM,SAAS,EAAC,oBAAoB,iBAAa,MAAM,GAAG,wBAEzD,CACJ,CAAC,CAAC,CAAC,CACF,wBAAwB,CACzB,GACM,IACL,CACP,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugly-app",
3
- "version": "0.1.584",
3
+ "version": "0.1.585",
4
4
  "type": "module",
5
5
  "main": "./dist/server/index.js",
6
6
  "exports": {
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by prebuild — do not edit manually
2
- export const CLI_VERSION = "0.1.584";
2
+ export const CLI_VERSION = "0.1.585";
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, useState } from 'react';
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
2
 
3
3
  const UGLY_BOT_URL =
4
4
  typeof window !== 'undefined'
@@ -14,12 +14,19 @@ interface LoginPopupProps {
14
14
 
15
15
  export function LoginPopup({ onSuccess, inline = false }: LoginPopupProps) {
16
16
  const [status, setStatus] = useState<'idle' | 'waiting' | 'error'>('idle');
17
+ // An OAuth code is single-use — redeem it (popup OR redirect path) exactly
18
+ // once, even if an effect re-runs (e.g. an unmemoized `onSuccess`) before the
19
+ // async verify resolves. A second redemption of the consumed code makes
20
+ // ugly.bot return "Code expired or not found" and login fails.
21
+ const redeemedRef = useRef(false);
17
22
 
18
23
  useEffect(() => {
19
24
  const handler = async (event: MessageEvent) => {
20
25
  if (event.origin !== UGLY_BOT_URL) return;
21
26
  const data = event.data as { type?: string; code?: string } | null;
22
27
  if (data?.type !== 'ugly-bot-oauth' || !data.code) return;
28
+ if (redeemedRef.current) return;
29
+ redeemedRef.current = true;
23
30
 
24
31
  setStatus('waiting');
25
32
  try {
@@ -45,9 +52,11 @@ export function LoginPopup({ onSuccess, inline = false }: LoginPopupProps) {
45
52
  // way the popup path does, then strip the param so a refresh can't replay a
46
53
  // consumed (deleted) code.
47
54
  useEffect(() => {
55
+ if (redeemedRef.current) return;
48
56
  const params = new URLSearchParams(window.location.search);
49
57
  const code = params.get('ugly_oauth_code');
50
58
  if (!code) return;
59
+ redeemedRef.current = true;
51
60
  setStatus('waiting');
52
61
  (async () => {
53
62
  try {