@instroc/auth 1.2.0 → 1.3.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.
@@ -1,5 +1,8 @@
1
1
  // src/forms/use-login-form.ts
2
- import { useState as useState2, useCallback as useCallback2, useRef as useRef2, useEffect as useEffect2 } from "react";
2
+ import { useState as useState3, useCallback as useCallback2, useRef as useRef2, useEffect as useEffect3 } from "react";
3
+
4
+ // src/use-auth.ts
5
+ import { useEffect as useEffect2, useState as useState2 } from "react";
3
6
 
4
7
  // src/provider.tsx
5
8
  import {
@@ -29,7 +32,7 @@ function authErrorFromResponse(response, data, fallbackMessage) {
29
32
 
30
33
  // src/version.ts
31
34
  var SDK_NAME = "instroc-auth";
32
- var SDK_VERSION = true ? "1.2.0" : "0.0.0-dev";
35
+ var SDK_VERSION = true ? "1.3.0" : "0.0.0-dev";
33
36
  function withSdkHeader(init) {
34
37
  const headers = new Headers(init?.headers ?? {});
35
38
  headers.set("X-Instroc-SDK", `${SDK_NAME}/${SDK_VERSION}`);
@@ -605,6 +608,43 @@ function useIsOwner() {
605
608
  const claim = decodeIsOwnerClaim(session.access_token);
606
609
  return claim === true;
607
610
  }
611
+ function useIsWorkspaceMember() {
612
+ const [isMember, setIsMember] = useState2(() => isDevHost());
613
+ useEffect2(() => {
614
+ if (typeof window === "undefined")
615
+ return;
616
+ let cancelled = false;
617
+ (async () => {
618
+ try {
619
+ const res = await fetch("/__instroc/workspace-status", {
620
+ credentials: "include",
621
+ cache: "no-store"
622
+ });
623
+ if (cancelled)
624
+ return;
625
+ if (!res.ok) {
626
+ setIsMember(isDevHost());
627
+ return;
628
+ }
629
+ const data = await res.json();
630
+ setIsMember(data.isWorkspaceMember === true);
631
+ } catch {
632
+ if (!cancelled)
633
+ setIsMember(isDevHost());
634
+ }
635
+ })();
636
+ return () => {
637
+ cancelled = true;
638
+ };
639
+ }, []);
640
+ return isMember;
641
+ }
642
+ function isDevHost() {
643
+ if (typeof window === "undefined")
644
+ return false;
645
+ const h = window.location.hostname;
646
+ return h === "localhost" || h === "127.0.0.1" || h.endsWith(".fly.dev") || h.endsWith(".workers.dev");
647
+ }
608
648
  function decodeIsOwnerClaim(accessToken) {
609
649
  try {
610
650
  const parts = accessToken.split(".");
@@ -647,10 +687,10 @@ function applyErrorResult(err, onError, messages, fallback, setError) {
647
687
  function useLoginForm(options = {}) {
648
688
  const { login, user } = useAuth();
649
689
  const { onSuccess, onNeedsVerification, onError, messages } = options;
650
- const [loading, setLoading] = useState2(false);
651
- const [error, setError] = useState2(null);
690
+ const [loading, setLoading] = useState3(false);
691
+ const [error, setError] = useState3(null);
652
692
  const mountedRef = useRef2(true);
653
- useEffect2(() => {
693
+ useEffect3(() => {
654
694
  mountedRef.current = true;
655
695
  return () => {
656
696
  mountedRef.current = false;
@@ -702,14 +742,14 @@ function useLoginForm(options = {}) {
702
742
  }
703
743
 
704
744
  // src/forms/use-signup-form.ts
705
- import { useState as useState3, useCallback as useCallback3, useRef as useRef3, useEffect as useEffect3 } from "react";
745
+ import { useState as useState4, useCallback as useCallback3, useRef as useRef3, useEffect as useEffect4 } from "react";
706
746
  function useSignupForm(options = {}) {
707
747
  const { signup } = useAuth();
708
748
  const { onSuccess, onNeedsVerification, onNeedsApproval, onError, messages } = options;
709
- const [loading, setLoading] = useState3(false);
710
- const [error, setError] = useState3(null);
749
+ const [loading, setLoading] = useState4(false);
750
+ const [error, setError] = useState4(null);
711
751
  const mountedRef = useRef3(true);
712
- useEffect3(() => {
752
+ useEffect4(() => {
713
753
  mountedRef.current = true;
714
754
  return () => {
715
755
  mountedRef.current = false;
@@ -766,7 +806,7 @@ function useSignupForm(options = {}) {
766
806
  }
767
807
 
768
808
  // src/forms/use-otp-form.ts
769
- import { useState as useState4, useCallback as useCallback4, useRef as useRef4, useEffect as useEffect4 } from "react";
809
+ import { useState as useState5, useCallback as useCallback4, useRef as useRef4, useEffect as useEffect5 } from "react";
770
810
  function useOtpForm(options) {
771
811
  const { verifyOTP, resendOTP } = useAuth();
772
812
  const {
@@ -777,18 +817,18 @@ function useOtpForm(options) {
777
817
  messages,
778
818
  resendCooldownSeconds = 60
779
819
  } = options;
780
- const [loading, setLoading] = useState4(false);
781
- const [resending, setResending] = useState4(false);
782
- const [resendCooldown, setResendCooldown] = useState4(0);
783
- const [error, setError] = useState4(null);
820
+ const [loading, setLoading] = useState5(false);
821
+ const [resending, setResending] = useState5(false);
822
+ const [resendCooldown, setResendCooldown] = useState5(0);
823
+ const [error, setError] = useState5(null);
784
824
  const mountedRef = useRef4(true);
785
- useEffect4(() => {
825
+ useEffect5(() => {
786
826
  mountedRef.current = true;
787
827
  return () => {
788
828
  mountedRef.current = false;
789
829
  };
790
830
  }, []);
791
- useEffect4(() => {
831
+ useEffect5(() => {
792
832
  if (resendCooldown <= 0)
793
833
  return;
794
834
  const id = setTimeout(() => setResendCooldown((s) => s - 1), 1e3);
@@ -870,15 +910,15 @@ function useOtpForm(options) {
870
910
  }
871
911
 
872
912
  // src/forms/use-forgot-password-form.ts
873
- import { useState as useState5, useCallback as useCallback5, useRef as useRef5, useEffect as useEffect5 } from "react";
913
+ import { useState as useState6, useCallback as useCallback5, useRef as useRef5, useEffect as useEffect6 } from "react";
874
914
  function useForgotPasswordForm(options = {}) {
875
915
  const { forgotPassword } = useAuth();
876
916
  const { onSuccess, onError, messages } = options;
877
- const [loading, setLoading] = useState5(false);
878
- const [submitted, setSubmitted] = useState5(false);
879
- const [error, setError] = useState5(null);
917
+ const [loading, setLoading] = useState6(false);
918
+ const [submitted, setSubmitted] = useState6(false);
919
+ const [error, setError] = useState6(null);
880
920
  const mountedRef = useRef5(true);
881
- useEffect5(() => {
921
+ useEffect6(() => {
882
922
  mountedRef.current = true;
883
923
  return () => {
884
924
  mountedRef.current = false;
@@ -933,14 +973,14 @@ function useForgotPasswordForm(options = {}) {
933
973
  }
934
974
 
935
975
  // src/forms/use-reset-password-form.ts
936
- import { useState as useState6, useCallback as useCallback6, useRef as useRef6, useEffect as useEffect6 } from "react";
976
+ import { useState as useState7, useCallback as useCallback6, useRef as useRef6, useEffect as useEffect7 } from "react";
937
977
  function useResetPasswordForm(options) {
938
978
  const { resetPassword } = useAuth();
939
979
  const { token, onSuccess, onError, messages } = options;
940
- const [loading, setLoading] = useState6(false);
941
- const [error, setError] = useState6(null);
980
+ const [loading, setLoading] = useState7(false);
981
+ const [error, setError] = useState7(null);
942
982
  const mountedRef = useRef6(true);
943
- useEffect6(() => {
983
+ useEffect7(() => {
944
984
  mountedRef.current = true;
945
985
  return () => {
946
986
  mountedRef.current = false;
@@ -987,6 +1027,7 @@ export {
987
1027
  useSession,
988
1028
  useAuthRequired,
989
1029
  useIsOwner,
1030
+ useIsWorkspaceMember,
990
1031
  useLoginForm,
991
1032
  useSignupForm,
992
1033
  useOtpForm,
package/dist/forms.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  useOtpForm,
5
5
  useResetPasswordForm,
6
6
  useSignupForm
7
- } from "./chunk-E6J2FYDA.js";
7
+ } from "./chunk-WPSPVFVM.js";
8
8
  export {
9
9
  useForgotPasswordForm,
10
10
  useLoginForm,
package/dist/index.d.ts CHANGED
@@ -34,6 +34,35 @@ declare function useAuthRequired(): {
34
34
  * Returns `false` while loading or when unauthenticated.
35
35
  */
36
36
  declare function useIsOwner(): boolean;
37
+ /**
38
+ * Minimal JWT payload decoder — no signature check (the server already
39
+ * verified it). Returns undefined if the token is malformed. We only read
40
+ * the `is_owner` boolean claim, which the BaaS auth signer sets from
41
+ * `_users.is_owner`.
42
+ */
43
+ /**
44
+ * Returns true if the current visitor is a member of the Instroc workspace
45
+ * that owns this published app. Used by premium templates to gate owner-only
46
+ * UI such as `/admin` or `/studio` routes.
47
+ *
48
+ * This is distinct from {@link useIsOwner} — workspace members are the humans
49
+ * who built the project (via Instroc), whereas app users (BaaS `_users`) are
50
+ * the end users of the published app. The two populations never overlap, so
51
+ * `useIsOwner` always returns `false` for regular app visitors. Admin UI on
52
+ * premium templates should gate on this hook instead.
53
+ *
54
+ * Implementation: calls `GET /__instroc/workspace-status` on the subdomain
55
+ * router, which validates the `instroc_wa` cookie (HttpOnly, set after
56
+ * Instroc workspace login) against the project's `workspaceId`. The same
57
+ * cookie is what the privacy gate uses to allow workspace members into
58
+ * private apps.
59
+ *
60
+ * Dev/preview note: when running on `localhost`, `*.fly.dev`, or
61
+ * `*.workers.dev` (dev-session HMR, editor preview), the hook returns `true`
62
+ * so template authors can see admin UI while building. In production
63
+ * (`*.instroc.app` and custom domains) the server answer is authoritative.
64
+ */
65
+ declare function useIsWorkspaceMember(): boolean;
37
66
 
38
67
  /**
39
68
  * Typed error thrown by every `@instroc/auth` method when the server responds
@@ -66,4 +95,4 @@ declare class AuthError extends Error {
66
95
  constructor(message: string, status: number, code?: string);
67
96
  }
68
97
 
69
- export { AuthContextValue, AuthError, AuthProvider, AuthProviderProps, AuthSession, AuthUser, useAuth, useAuthContext, useAuthRequired, useIsOwner, useSession, useUser };
98
+ export { AuthContextValue, AuthError, AuthProvider, AuthProviderProps, AuthSession, AuthUser, useAuth, useAuthContext, useAuthRequired, useIsOwner, useIsWorkspaceMember, useSession, useUser };
package/dist/index.js CHANGED
@@ -6,13 +6,14 @@ import {
6
6
  useAuthRequired,
7
7
  useForgotPasswordForm,
8
8
  useIsOwner,
9
+ useIsWorkspaceMember,
9
10
  useLoginForm,
10
11
  useOtpForm,
11
12
  useResetPasswordForm,
12
13
  useSession,
13
14
  useSignupForm,
14
15
  useUser
15
- } from "./chunk-E6J2FYDA.js";
16
+ } from "./chunk-WPSPVFVM.js";
16
17
  export {
17
18
  AuthError,
18
19
  AuthProvider,
@@ -21,6 +22,7 @@ export {
21
22
  useAuthRequired,
22
23
  useForgotPasswordForm,
23
24
  useIsOwner,
25
+ useIsWorkspaceMember,
24
26
  useLoginForm,
25
27
  useOtpForm,
26
28
  useResetPasswordForm,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instroc/auth",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Authentication hooks for Instroc Cloud — useAuth, useUser, AuthProvider, and headless form hooks",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -18,6 +18,10 @@
18
18
  "files": [
19
19
  "dist"
20
20
  ],
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "dev": "tsup --watch"
24
+ },
21
25
  "peerDependencies": {
22
26
  "react": "^18.0.0"
23
27
  },
@@ -35,9 +39,5 @@
35
39
  },
36
40
  "publishConfig": {
37
41
  "access": "public"
38
- },
39
- "scripts": {
40
- "build": "tsup",
41
- "dev": "tsup --watch"
42
42
  }
43
- }
43
+ }