@wzyjs/middle-sdk 0.3.36

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 ADDED
@@ -0,0 +1,323 @@
1
+ # @wzyjs/middle-sdk
2
+
3
+ ## Login
4
+
5
+ 业务项目通过 `@wzyjs/middle-sdk/login` 接入中台登录。
6
+
7
+ ### 创建 NextAuth 配置
8
+
9
+ ```ts
10
+ import { createMiddleNextAuthOptions } from '@wzyjs/middle-sdk/login'
11
+
12
+ export const authOptions = createMiddleNextAuthOptions({
13
+ cookiePrefix: 'my-app',
14
+ clientId: process.env.MIDDLE_OIDC_CLIENT_ID!,
15
+ clientSecret: process.env.MIDDLE_OIDC_CLIENT_SECRET!,
16
+ issuer: process.env.MIDDLE_OIDC_ISSUER!,
17
+ secret: process.env.NEXTAUTH_SECRET!,
18
+ pages: {
19
+ signIn: '/auth/signin',
20
+ error: '/auth/error',
21
+ },
22
+ })
23
+ ```
24
+
25
+ ### 配置参数
26
+
27
+ ```ts
28
+ interface CreateMiddleNextAuthOptions {
29
+ // 中台 OAuth client id。
30
+ clientId: string
31
+ // 中台 OAuth client secret。
32
+ clientSecret: string
33
+ // 中台站点地址,例如 https://middle.example.com。
34
+ issuer: string
35
+ // NextAuth secret。
36
+ secret: string
37
+ // Cookie 名称前缀,用来避免多个业务系统的 NextAuth cookie 冲突。
38
+ cookiePrefix?: string
39
+ // 是否开启 NextAuth debug 日志。
40
+ debug?: boolean
41
+ // NextAuth 登录页、错误页等页面配置。
42
+ pages?: NextAuthOptions['pages']
43
+ // 是否给 NextAuth cookie 添加 secure 标记。
44
+ secureCookies?: boolean
45
+ }
46
+ ```
47
+
48
+ ### Session 参数
49
+
50
+ 登录成功后,业务项目可以从 session 里读取这些参数:
51
+
52
+ ```ts
53
+ session.middleAccessToken
54
+ session.app
55
+ session.pages
56
+ session.permissions
57
+ session.roles
58
+ ```
59
+
60
+ ### 补充 Session 类型
61
+
62
+ NextAuth 默认的 `Session` 类型不知道 SDK 增加的中台字段。
63
+ 业务项目如果要在 TypeScript 里安全访问 `session.middleAccessToken`、`session.app`、`session.pages` 等字段,需要补充一次类型声明。
64
+
65
+ ```ts
66
+ import type { DefaultSession } from 'next-auth'
67
+ import type { MiddleNextAuthSessionFields } from '@wzyjs/middle-sdk/login'
68
+
69
+ declare module 'next-auth' {
70
+ interface Session extends MiddleNextAuthSessionFields {
71
+ user?: DefaultSession['user'] & {
72
+ id?: string
73
+ }
74
+ }
75
+ }
76
+ ```
77
+
78
+ ### 手动刷新权限资料
79
+
80
+ 业务项目需要重新读取中台应用、页面、角色、权限时,可以调用:
81
+
82
+ ```ts
83
+ import { createMiddleAccessProfileRefreshUpdate } from '@wzyjs/middle-sdk/login'
84
+ import { useSession } from 'next-auth/react'
85
+
86
+ const { update } = useSession()
87
+
88
+ await update(createMiddleAccessProfileRefreshUpdate())
89
+ ```
90
+
91
+ ## Page
92
+
93
+ 业务项目通过 `@wzyjs/middle-sdk/page` 处理中台页面配置。
94
+
95
+ ### 模块功能
96
+
97
+ - 把 `session.pages` 里的页面配置解析成菜单可直接使用的数据。
98
+ - 自动区分站内路由、iframe 外链、新开窗口外链。
99
+ - 提供中台 iframe 承载页组件。
100
+
101
+ ### 解析菜单页面
102
+
103
+ ```ts
104
+ import { resolveMiddleMenuPages } from '@wzyjs/middle-sdk/page'
105
+
106
+ const pathname = usePathname()
107
+
108
+ const menuItems = resolveMiddleMenuPages(session.pages, {
109
+ // iframe 承载页自己的路由地址。
110
+ // 当中台页面是外链但不走新开窗口时,SDK 会把它解析成 `/middle?url=...` 这种地址。
111
+ iframePagePath: '/middle',
112
+ // 当前页面 pathname,用来判断菜单是否处于激活状态。
113
+ // 站内路由菜单会拿它和 page.path 对比。
114
+ pathname,
115
+ // 当前 iframe 承载页里实际打开的外链地址。
116
+ // 当 pathname 是 `/middle` 时,SDK 会继续用这个值判断当前激活的是哪个 iframe 菜单。
117
+ iframeUrl: searchParams.get('url'),
118
+ })
119
+ ```
120
+
121
+ 返回结果里的每一项都会带这些字段:
122
+
123
+ ```ts
124
+ interface MiddleResolvedPage {
125
+ href: string
126
+ isActive: boolean
127
+ isBlank: boolean
128
+ isExternal: boolean
129
+ isIframe: boolean
130
+ isRoute: boolean
131
+ page: MiddlePage
132
+ rel?: 'noreferrer'
133
+ target?: '_blank'
134
+ type: 'route' | 'iframe' | 'blank'
135
+ }
136
+ ```
137
+
138
+ ### 挂载 iframe 页面
139
+
140
+ ```tsx
141
+ import { Suspense } from 'react'
142
+ import { MiddleIframeRoutePage } from '@wzyjs/middle-sdk/page'
143
+
144
+ export default function MiddlePage() {
145
+ return (
146
+ <Suspense>
147
+ <MiddleIframeRoutePage />
148
+ </Suspense>
149
+ )
150
+ }
151
+ ```
152
+
153
+ 默认会从当前地址的 `url` query 参数读取 iframe 地址,例如 `/middle?url=https://example.com`。
154
+
155
+ ### 类型
156
+
157
+ ```ts
158
+ interface MiddlePage {
159
+ icon: string | null
160
+ isMenu: boolean
161
+ name: string
162
+ openMode: 'iframe' | 'blank' | null
163
+ path: string
164
+ permissions: string[]
165
+ }
166
+ ```
167
+
168
+ ## Proxy
169
+
170
+ 业务项目通过 `@wzyjs/middle-sdk/proxy` 生成中台代理地址。
171
+
172
+ ### 生成代理地址
173
+
174
+ ```ts
175
+ import { createMiddleProxyUrlFactory } from '@wzyjs/middle-sdk/proxy'
176
+
177
+ const createAppProxyUrl = createMiddleProxyUrlFactory({
178
+ appid: 'ertyuertyu',
179
+ baseUrl: 'https://middle.example.com',
180
+ })
181
+
182
+ const proxyUrl = createAppProxyUrl('https://example.com/path?a=1')
183
+
184
+ // https://middle.example.com/api/proxy/my-app?url=https%3A%2F%2Fexample.com%2Fpath%3Fa%3D1
185
+ ```
186
+
187
+ ### 参数
188
+
189
+ ```ts
190
+ interface CreateMiddleProxyUrlParams {
191
+ url: string
192
+ appid: string
193
+ baseUrl: string
194
+ }
195
+
196
+ interface CreateMiddleProxyUrlFactoryParams {
197
+ appid: string
198
+ baseUrl: string
199
+ }
200
+ ```
201
+
202
+ ### 规则
203
+
204
+ - 只会代理 `http` 和 `https` 地址。
205
+ - 已经是 `/api/proxy/...` 的地址会原样返回,不会重复包装。
206
+ - `appid` 为空时会直接抛错。
207
+ - `baseUrl` 为空时会直接抛错。
208
+ - 返回值始终是带中台域名的完整代理地址。
209
+
210
+ ## Upload
211
+
212
+ 业务项目通过 `@wzyjs/middle-sdk/upload` 上传文件到中台。
213
+
214
+ ### JS 上传二进制文件
215
+
216
+ ```ts
217
+ import { createMiddleUploadFileFactory } from '@wzyjs/middle-sdk/upload'
218
+
219
+ const uploadMiddleFile = createMiddleUploadFileFactory({
220
+ baseUrl: 'https://middle.example.com',
221
+ accessToken: 'your-access-token',
222
+ })
223
+
224
+ const result = await uploadMiddleFile({
225
+ file: fileInput.files![0],
226
+ fileName: 'avatar.png',
227
+ mimeType: 'image/png',
228
+ business: 'avatar',
229
+ })
230
+ ```
231
+
232
+ ### JS 上传 URL 文件源
233
+
234
+ ```ts
235
+ import { createMiddleUploadFileFactory } from '@wzyjs/middle-sdk/upload'
236
+
237
+ const uploadMiddleFile = createMiddleUploadFileFactory({
238
+ baseUrl: 'https://middle.example.com',
239
+ accessToken: async () => session.middleAccessToken!,
240
+ })
241
+
242
+ const result = await uploadMiddleFile({
243
+ type: 'url',
244
+ file: 'https://example.com/image.png',
245
+ business: 'avatar',
246
+ })
247
+ ```
248
+
249
+ ### 给上传组件使用
250
+
251
+ ```ts
252
+ import { createMiddleUploadPropsFactory } from '@wzyjs/middle-sdk/upload'
253
+
254
+ const createUploadProps = createMiddleUploadPropsFactory({
255
+ baseUrl: 'https://middle.example.com',
256
+ accessToken: session.middleAccessToken,
257
+ })
258
+
259
+ <FileUploader
260
+ immediateUpload
261
+ trigger='button'
262
+ showUploadList={false}
263
+ {...createUploadProps({ business: 'file' })}
264
+ />
265
+ ```
266
+
267
+ ### 参数
268
+
269
+ ```ts
270
+ interface CreateMiddleUploadFileFactoryParams {
271
+ baseUrl: string
272
+ accessToken: string | (() => Promise<string> | string)
273
+ }
274
+
275
+ interface CreateMiddleUploadPropsFactoryParams {
276
+ baseUrl: string
277
+ accessToken?: string | null
278
+ }
279
+
280
+ interface CreateMiddleUploadPropsParams {
281
+ business?: string
282
+ }
283
+
284
+ type MiddleUploadFileInput =
285
+ | {
286
+ type?: 'file'
287
+ file: Blob | ArrayBuffer | Uint8Array
288
+ business?: string
289
+ fileName?: string
290
+ mimeType?: string
291
+ }
292
+ | {
293
+ type: 'url'
294
+ file: string
295
+ business?: string
296
+ fileName?: string
297
+ mimeType?: string
298
+ }
299
+
300
+ ```
301
+
302
+ ### 返回值
303
+
304
+ ```ts
305
+ interface MiddleUploadedFile {
306
+ id: string
307
+ name: string
308
+ mimeType: string
309
+ ext: string
310
+ size: number
311
+ hash: string
312
+ objectKey: string
313
+ provider: string
314
+ status: string
315
+ business: string | null
316
+ applicationId: string
317
+ creatorId: string
318
+ url: string
319
+ createdAt: string | Date
320
+ updatedAt: string | Date
321
+ isDeleted: boolean
322
+ }
323
+ ```
@@ -0,0 +1,7 @@
1
+ import { type TRPCClient } from '@trpc/client';
2
+ import type { MiddleClientOptions } from '../utils/options';
3
+ export type { MiddleClientOptions };
4
+ export { resolveAccessToken, trimTrailingSlash } from '../utils/options';
5
+ export type MiddleApiRouter = any;
6
+ export type MiddleApi = TRPCClient<MiddleApiRouter>;
7
+ export declare const createMiddleApi: (option: MiddleClientOptions) => MiddleApi;
@@ -0,0 +1,2 @@
1
+ // @ts-nocheck
2
+ import{createTRPCProxyClient as l}from"@trpc/client";import{httpBatchLink as a}from"@trpc/client";import c from"superjson";var t=(e)=>e.replace(/\/+$/,""),n=async(e)=>{if(typeof e.accessToken==="function")return e.accessToken();return e.accessToken};var i=(e)=>{let r=t(e.baseUrl);return a({fetch:e.fetch,transformer:c,url:`${r}/api/trpc`,headers:async()=>{let s=await n(e.getClientOption?.()??e);return s?{Authorization:`Bearer ${s}`}:{}}})};var m=(e)=>l({links:[i(e)]});export{t as trimTrailingSlash,n as resolveAccessToken,m as createMiddleApi};
@@ -0,0 +1,17 @@
1
+ import { type CreateTRPCReact } from '@trpc/react-query';
2
+ import type { AnyTRPCRootTypes, TRPCBuiltRouter } from '@trpc/server';
3
+ import { QueryClient } from '@tanstack/react-query';
4
+ import { type ReactElement, type ReactNode } from 'react';
5
+ import type { MiddleClientOptions } from '../utils/options';
6
+ export type ApiRouter = TRPCBuiltRouter<AnyTRPCRootTypes, Record<never, never>>;
7
+ export interface MiddleProviderProps extends MiddleClientOptions {
8
+ children: ReactNode;
9
+ queryClient?: QueryClient;
10
+ }
11
+ export type MiddleReactApi = CreateTRPCReact<ApiRouter, unknown>;
12
+ export type MiddleReactBinding<TApi extends {
13
+ Provider: unknown;
14
+ }> = Omit<TApi, 'Provider'> & {
15
+ Provider: (props: MiddleProviderProps) => ReactElement;
16
+ };
17
+ export declare const middle: MiddleReactBinding<MiddleReactApi>;
@@ -0,0 +1,3 @@
1
+ // @ts-nocheck
2
+ 'use client';
3
+ import{createTRPCReact as B}from"@trpc/react-query";import{QueryClient as L,QueryClientProvider as S}from"@tanstack/react-query";import{createContext as $,useMemo as r,useRef as H,useState as b}from"react";import{httpBatchLink as m}from"@trpc/client";import M from"superjson";var O=(n)=>n.replace(/\/+$/,""),T=async(n)=>{if(typeof n.accessToken==="function")return n.accessToken();return n.accessToken};var h=(n)=>{let t=O(n.baseUrl);return m({fetch:n.fetch,transformer:M,url:`${t}/api/trpc`,headers:async()=>{let s=await T(n.getClientOption?.()??n);return s?{Authorization:`Bearer ${s}`}:{}}})};import{jsx as a}from"react/jsx-runtime";var c=(n)=>{let{accessToken:t,baseUrl:s,children:f,fetch:e,queryClient:g,trpcApi:o}=n,l=H({accessToken:t,baseUrl:s,fetch:e});l.current={accessToken:t,baseUrl:s,fetch:e};let[k]=b(()=>new L),i=g??k,d=r(()=>o.createClient({links:[h({...l.current,getClientOption:()=>l.current})]}),[s,e,o]);return a(S,{client:i,children:a(o.Provider,{client:d,queryClient:i,children:f})})},x=(n)=>{let t=Object.create(n);return t.Provider=(s)=>a(c,{...s,trpcApi:n}),t},I=x(B({context:$(null)}));export{I as middle};
@@ -0,0 +1,8 @@
1
+ export type MaybePromise<T> = Promise<T> | T;
2
+ export interface MiddleClientOptions {
3
+ baseUrl: string;
4
+ accessToken?: string | null | (() => MaybePromise<string | null | undefined>);
5
+ fetch?: typeof fetch;
6
+ }
7
+ export declare const trimTrailingSlash: (value: string) => string;
8
+ export declare const resolveAccessToken: (option: MiddleClientOptions) => Promise<string | null | undefined>;
@@ -0,0 +1,6 @@
1
+ import { type MiddleClientOptions } from './options';
2
+ interface CreateMiddleHttpBatchLinkOptions extends MiddleClientOptions {
3
+ getClientOption?: () => MiddleClientOptions;
4
+ }
5
+ export declare const createMiddleHttpBatchLink: (option: CreateMiddleHttpBatchLinkOptions) => import("@trpc/client").TRPCLink<import("@trpc/server").AnyRouter>;
6
+ export {};
@@ -0,0 +1,3 @@
1
+ export * from './next-auth';
2
+ export * from './refresh-update';
3
+ export type * from './types';
@@ -0,0 +1,2 @@
1
+ // @ts-nocheck
2
+ var S=["iframe","blank"],c=(e)=>Boolean(e)&&typeof e==="object"&&!Array.isArray(e),i=(e)=>typeof e==="string"?e:"",l=(e)=>typeof e==="string"?e:null,z=(e)=>Array.isArray(e)?e.filter((t)=>typeof t==="string"):[],A=(e)=>{if(!c(e))return;let t=i(e.id),s=i(e.identifier),o=i(e.name);if(!t||!s||!o)return;return{id:t,identifier:s,name:o}},C=(e)=>S.includes(e)?e:null,d=(e)=>{if(!Array.isArray(e))return[];return e.flatMap((t)=>{if(!c(t))return[];let s=i(t.name),o=i(t.path);if(!s||!o)return[];return[{icon:l(t.icon),isMenu:Boolean(t.isMenu),name:s,openMode:C(t.openMode),path:o,permissions:z(t.permissions)}]})},h=(e)=>{if(!Array.isArray(e))return[];return e.flatMap((t)=>{if(!c(t))return[];let s=i(t.key),o=i(t.name);if(!s||!o)return[];return[{description:l(t.description),key:s,name:o}]})},M=(e)=>h(e),y=(e)=>h(e);var F=async(e,t)=>{if(typeof t!=="string"||!t)return{app:void 0,pages:[],permissions:[],roles:[]};let s=`${e.replace(/\/$/,"")}/api/oauth/userinfo`,o=await fetch(s,{cache:"no-store",headers:{Authorization:`Bearer ${t}`}});if(!o.ok)throw Error("Failed to load middle userinfo");let r=await o.json();return{app:A(r.app),pages:d(r.pages),permissions:M(r.permissions),roles:y(r.roles)}},f=async(e,t)=>{let s=await F(e,t.middleAccessToken);t.app=s.app,t.pages=s.pages,t.permissions=s.permissions,t.roles=s.roles},p=(e)=>{e.app=void 0,e.pages=[],e.permissions=[],e.roles=[]};var k=()=>({middleAction:"middle:access-profile:refresh"}),g=(e)=>Boolean(e)&&typeof e==="object"&&!Array.isArray(e)&&e.middleAction==="middle:access-profile:refresh";import{Buffer as E}from"node:buffer";var I=300000,x=(e)=>typeof e==="number"&&Number.isFinite(e)&&Date.now()+I<e,u=(e)=>{let t=e;if(typeof t.expires_at==="number"&&Number.isFinite(t.expires_at))return t.expires_at*1000;if(typeof t.expires_in==="number"&&Number.isFinite(t.expires_in))return Date.now()+t.expires_in*1000;return},m=async(e)=>{let t=await fetch(`${e.issuer}/api/oauth/token`,{body:new URLSearchParams({grant_type:"refresh_token",refresh_token:e.refreshToken}),cache:"no-store",headers:{Authorization:`Basic ${E.from(`${e.clientId}:${e.clientSecret}`).toString("base64")}`,"Content-Type":"application/x-www-form-urlencoded"},method:"POST"}),s=await t.json().catch(()=>({}));if(!t.ok||typeof s.access_token!=="string"||!s.access_token)throw Error("刷新中台 access token 失败");return{accessToken:s.access_token,accessTokenExpiresAt:u(s),refreshToken:typeof s.refresh_token==="string"&&s.refresh_token?s.refresh_token:e.refreshToken}};var U=(e,t)=>{let s=t;if(typeof s?.sub==="string")e.sub=s.sub;if(typeof s?.email==="string")e.email=s.email;if(typeof s?.name==="string")e.name=s.name;if(typeof s?.picture==="string")e.picture=s.picture},j=(e,t)=>{let s=t;if(!s?.access_token)return;if(e.middleAccessToken=s.access_token,e.middleAccessTokenExpiresAt=u(t),e.middleAuthError=void 0,typeof s.refresh_token==="string"&&s.refresh_token)e.middleRefreshToken=s.refresh_token},w=(e)=>{if(!e.middleAccessTokenExpiresAt||Date.now()>=e.middleAccessTokenExpiresAt)e.middleAccessToken=void 0,p(e)},B=async(e,t,s)=>{if(!g(s)||!t.middleAccessToken)return!1;try{await f(e,t),t.middleAuthError=void 0}catch(o){p(t),t.middleAuthError="access_profile_refresh_failed"}return!0},b=(e,t,s)=>async({token:o,account:r,profile:O,session:R,trigger:$})=>{let n=o;if(U(n,O),j(n,r),!r?.access_token&&n.middleAccessToken&&!x(n.middleAccessTokenExpiresAt)){if(!n.middleRefreshToken)return w(n),n;try{let a=await m({clientId:t,clientSecret:s,issuer:e,refreshToken:n.middleRefreshToken});n.middleAccessToken=a.accessToken,n.middleAccessTokenExpiresAt=a.accessTokenExpiresAt,n.middleRefreshToken=a.refreshToken,n.middleAuthError=void 0}catch(a){return w(n),n.middleAuthError="access_token_refresh_failed",n}}if($==="update"&&await B(e,n,R))return n;if(n.middleAccessToken&&(r?.access_token||!n.app))try{await f(e,n),n.middleAuthError=void 0}catch(a){if(r?.access_token)throw a;p(n),n.middleAuthError="access_profile_refresh_failed"}return n};var N=()=>async({session:e,token:t})=>{let s=e,o=s.user,r=t;if(s.user&&r.sub)o.id=r.sub;if(s.user)s.user.email=r.email,s.user.name=r.name,s.user.image=typeof r.picture==="string"?r.picture:null;return s.app=r.app,s.middleAuthError=r.middleAuthError,s.middleAccessToken=r.middleAccessToken,s.pages=r.pages??[],s.permissions=r.permissions??[],s.roles=r.roles??[],s};var P=(e,t,s)=>({jwt:b(e,t,s),session:N(),async redirect({url:o,baseUrl:r}){if(o.startsWith(r))return o;if(o.startsWith("/"))return`${r}${o}`;return r}});var _=(e,t)=>{let s={httpOnly:!0,path:"/",sameSite:"lax",secure:t};return{callbackUrl:{name:`${e}.callback-url`,options:s},csrfToken:{name:`${e}.csrf-token`,options:s},nonce:{name:`${e}.nonce`,options:s},pkceCodeVerifier:{name:`${e}.pkce.code-verifier`,options:{...s,maxAge:900}},sessionToken:{name:`${e}.session-token`,options:s},state:{name:`${e}.state`,options:{...s,maxAge:900}}}};var te=(e)=>{let t=e.issuer.replace(/\/$/,"");return{debug:e.debug??!1,cookies:!e.cookiePrefix?void 0:_(e.cookiePrefix,e.secureCookies??!1),pages:e.pages,secret:e.secret,callbacks:P(t,e.clientId,e.clientSecret),session:{strategy:"jwt"},providers:[{id:"middle",name:"中台登录",type:"oauth",issuer:t,clientId:e.clientId,clientSecret:e.clientSecret,checks:["pkce","state"],idToken:!0,jwks_endpoint:`${t}/api/oauth/jwks`,authorization:{url:`${t}/api/oauth/authorize`,params:{scope:"openid profile email offline_access"}},token:{url:`${t}/api/oauth/token`},userinfo:{url:`${t}/api/oauth/userinfo`},profile(s){return{email:s.email,id:s.sub,image:s.picture,name:s.name}}}]}};export{g as isMiddleAccessProfileRefreshUpdate,te as createMiddleNextAuthOptions,k as createMiddleAccessProfileRefreshUpdate};
@@ -0,0 +1,9 @@
1
+ import type { MiddleNextAuthJWTFields } from '../types';
2
+ export declare const getMiddleAccessProfile: (issuer: string, accessToken?: unknown) => Promise<{
3
+ app: import("..").MiddleAppInfo | undefined;
4
+ pages: import("../../page").MiddlePage[];
5
+ permissions: import("..").MiddleAppPermission[];
6
+ roles: import("..").MiddleAppRole[];
7
+ }>;
8
+ export declare const loadMiddleAccessProfile: (issuer: string, middleToken: MiddleNextAuthJWTFields) => Promise<void>;
9
+ export declare const clearMiddleAccessProfile: (middleToken: MiddleNextAuthJWTFields) => void;
@@ -0,0 +1,2 @@
1
+ import type { NextAuthOptions } from 'next-auth';
2
+ export declare const createCallbacks: (issuer: string, clientId: string, clientSecret: string) => NonNullable<NextAuthOptions["callbacks"]>;
@@ -0,0 +1,2 @@
1
+ import type { NextAuthOptions } from 'next-auth';
2
+ export declare const createCookies: (cookiePrefix: string, secureCookies: boolean) => NextAuthOptions["cookies"];
@@ -0,0 +1,4 @@
1
+ import type { NextAuthOptions } from 'next-auth';
2
+ type JwtCallback = NonNullable<NonNullable<NextAuthOptions['callbacks']>['jwt']>;
3
+ export declare const createJwtCallback: (issuer: string, clientId: string, clientSecret: string) => JwtCallback;
4
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { MiddleAppInfo, MiddleAppPermission, MiddleAppRole } from '../types';
2
+ import type { MiddlePage } from '../../page/types';
3
+ export declare const normalizeMiddleApp: (value: unknown) => MiddleAppInfo | undefined;
4
+ export declare const normalizeMiddlePages: (value: unknown) => MiddlePage[];
5
+ export declare const normalizeMiddlePermissions: (value: unknown) => MiddleAppPermission[];
6
+ export declare const normalizeMiddleRoles: (value: unknown) => MiddleAppRole[];
@@ -0,0 +1,4 @@
1
+ import type { NextAuthOptions } from 'next-auth';
2
+ type SessionCallback = NonNullable<NonNullable<NextAuthOptions['callbacks']>['session']>;
3
+ export declare const createSessionCallback: () => SessionCallback;
4
+ export {};
@@ -0,0 +1,12 @@
1
+ export declare const isMiddleAccessTokenFresh: (expiresAt?: number) => boolean;
2
+ export declare const getAccountAccessTokenExpiresAt: (account: unknown) => number | undefined;
3
+ export declare const refreshMiddleAccessToken: (option: {
4
+ clientId: string;
5
+ clientSecret: string;
6
+ issuer: string;
7
+ refreshToken: string;
8
+ }) => Promise<{
9
+ accessToken: string;
10
+ accessTokenExpiresAt: number | undefined;
11
+ refreshToken: string;
12
+ }>;
@@ -0,0 +1,3 @@
1
+ import type { NextAuthOptions } from 'next-auth';
2
+ import type { CreateMiddleNextAuthOptions } from './types';
3
+ export declare const createMiddleNextAuthOptions: (option: CreateMiddleNextAuthOptions) => NextAuthOptions;
@@ -0,0 +1,7 @@
1
+ declare const middleAccessProfileRefreshUpdateAction = "middle:access-profile:refresh";
2
+ export interface MiddleAccessProfileRefreshUpdate {
3
+ middleAction: typeof middleAccessProfileRefreshUpdateAction;
4
+ }
5
+ export declare const createMiddleAccessProfileRefreshUpdate: () => MiddleAccessProfileRefreshUpdate;
6
+ export declare const isMiddleAccessProfileRefreshUpdate: (value: unknown) => value is MiddleAccessProfileRefreshUpdate;
7
+ export {};
@@ -0,0 +1,47 @@
1
+ import type { NextAuthOptions } from 'next-auth';
2
+ import type { MiddlePage } from '../page/types';
3
+ export type MiddlePageOpenMode = NonNullable<MiddlePage['openMode']>;
4
+ export interface MiddleAppInfo {
5
+ id: string;
6
+ identifier: string;
7
+ name: string;
8
+ }
9
+ export interface MiddleAppPermission {
10
+ description: string | null;
11
+ key: string;
12
+ name: string;
13
+ }
14
+ export interface MiddleAppRole {
15
+ description: string | null;
16
+ key: string;
17
+ name: string;
18
+ }
19
+ export type MiddleNextAuthError = 'access_token_refresh_failed' | 'access_profile_refresh_failed';
20
+ export interface MiddleNextAuthSessionFields {
21
+ app?: MiddleAppInfo;
22
+ middleAuthError?: MiddleNextAuthError;
23
+ middleAccessToken?: string;
24
+ pages: MiddlePage[];
25
+ permissions: MiddleAppPermission[];
26
+ roles: MiddleAppRole[];
27
+ }
28
+ export interface MiddleNextAuthJWTFields {
29
+ app?: MiddleAppInfo;
30
+ middleAuthError?: MiddleNextAuthError;
31
+ middleAccessToken?: string;
32
+ middleAccessTokenExpiresAt?: number;
33
+ middleRefreshToken?: string;
34
+ pages?: MiddlePage[];
35
+ permissions?: MiddleAppPermission[];
36
+ roles?: MiddleAppRole[];
37
+ }
38
+ export interface CreateMiddleNextAuthOptions {
39
+ clientId: string;
40
+ clientSecret: string;
41
+ cookiePrefix?: string;
42
+ debug?: boolean;
43
+ issuer: string;
44
+ pages?: NextAuthOptions['pages'];
45
+ secret: string;
46
+ secureCookies?: boolean;
47
+ }
@@ -0,0 +1,9 @@
1
+ import type { CSSProperties, ReactNode } from 'react';
2
+ export interface MiddleIframeRoutePageProps {
3
+ className?: string;
4
+ style?: CSSProperties;
5
+ fallback?: ReactNode;
6
+ url?: string;
7
+ urlParamName?: string;
8
+ }
9
+ export declare const MiddleIframeRoutePage: (props: MiddleIframeRoutePageProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,3 @@
1
+ export declare const MiddleIframeRoutePage: (props: import("./MiddleIframeRoutePage").MiddleIframeRoutePageProps) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const resolveMiddleMenuPages: (pages: import("./types").MiddlePage[], option?: import("./types").ResolveMiddlePageOptions) => import("./types").MiddleResolvedPage[];
3
+ export type { MiddlePage, MiddleResolvedPage } from './types';
@@ -0,0 +1,3 @@
1
+ // @ts-nocheck
2
+ 'use client';
3
+ import{useSearchParams as R}from"next/navigation";var i=(e)=>/^https?:\/\/[^\s]+$/i.test(e),m=(e)=>/^\/[^\s]*$/.test(e),d=(e)=>i(e.path)&&e.openMode!=="blank",g=(e)=>i(e.path)&&e.openMode==="blank",u=(e,t={})=>{if(!d(e))return e.path;let r=new URLSearchParams({url:e.path});return`${t.iframePagePath??"/middle"}?${r.toString()}`},M=(e)=>{if(g(e))return"blank";if(d(e))return"iframe";return"route"},p=(e,t)=>{if(!t||!m(e.path))return!1;return t===e.path||e.path!=="/"&&t.startsWith(`${e.path}/`)},h=(e,t={})=>{let r=M(e),a=r==="blank",l=t.iframePagePath??"/middle",o=r==="iframe"?t.pathname===l&&t.iframeUrl===e.path:r==="route"?p(e,t.pathname):!1;return{href:u(e,t),isActive:o,isBlank:a,isExternal:i(e.path),isIframe:r==="iframe",isRoute:r==="route",page:e,rel:a?"noreferrer":void 0,target:a?"_blank":void 0,type:r}},P=(e,t={})=>{return e.filter((r)=>r.isMenu).map((r)=>h(r,t))};import{jsx as n}from"react/jsx-runtime";var v={background:"#fff",display:"flex",flexDirection:"column",height:"100%",minHeight:720},y={border:0,flex:1,minHeight:0},S={alignItems:"center",background:"#fff",color:"#6b7280",display:"flex",justifyContent:"center",minHeight:"100%",padding:"48px 24px"},f=(e)=>{let{className:t,fallback:r="页面地址无效",style:a,url:l,urlParamName:o="url"}=e,c=R(),s=l??c.get(o)??"";if(!s||!i(s))return n("div",{className:t,style:{...S,...a},children:r});return n("div",{className:t,style:{...v,...a},children:n("iframe",{src:s,style:y,title:s})})};var D=f,N=P;export{N as resolveMiddleMenuPages,D as MiddleIframeRoutePage};
@@ -0,0 +1,25 @@
1
+ export interface MiddlePage {
2
+ icon: string | null;
3
+ isMenu: boolean;
4
+ name: string;
5
+ openMode: 'iframe' | 'blank' | null;
6
+ path: string;
7
+ permissions: string[];
8
+ }
9
+ export interface ResolveMiddlePageOptions {
10
+ iframePagePath?: string;
11
+ iframeUrl?: string | null;
12
+ pathname?: string | null;
13
+ }
14
+ export interface MiddleResolvedPage {
15
+ href: string;
16
+ isActive: boolean;
17
+ isBlank: boolean;
18
+ isExternal: boolean;
19
+ isIframe: boolean;
20
+ isRoute: boolean;
21
+ page: MiddlePage;
22
+ rel?: 'noreferrer';
23
+ target?: '_blank';
24
+ type: 'route' | 'iframe' | 'blank';
25
+ }
@@ -0,0 +1,3 @@
1
+ import type { MiddlePage, MiddleResolvedPage, ResolveMiddlePageOptions } from './types';
2
+ export declare const isExternalPagePath: (path: string) => boolean;
3
+ export declare const resolveMiddleMenuPages: (pages: MiddlePage[], option?: ResolveMiddlePageOptions) => MiddleResolvedPage[];
@@ -0,0 +1,2 @@
1
+ import type { CreateMiddleProxyUrlFactoryParams } from './types';
2
+ export declare const createMiddleProxyUrlFactory: (params: CreateMiddleProxyUrlFactoryParams) => (url: string) => string;
@@ -0,0 +1,2 @@
1
+ // @ts-nocheck
2
+ var i=(r,t)=>{try{return new URL(r).toString()}catch{return new URL(r,t).toString()}},a=(r)=>{if(!r)return!1;if(r.startsWith("/api/proxy/"))return!0;try{return new URL(r).pathname.startsWith("/api/proxy/")}catch{return!1}},s=(r)=>{try{let t=new URL(r);return t.protocol==="http:"||t.protocol==="https:"}catch{return!1}},d=(r)=>`/api/proxy/${encodeURIComponent(r.appid)}?url=${encodeURIComponent(r.targetUrl)}`,n=(r)=>{let t=r.url.trim(),e=r.appid.trim(),o=r.baseUrl.trim();if(!t)throw Error("缺少 url");if(!e)throw Error("缺少 middle appid");if(!o)throw Error("缺少 middle baseUrl");if(a(t))return i(t,o);if(!s(t))return t;return i(d({appid:e,targetUrl:t}),o)};var c=(r)=>{let t=r.appid.trim(),e=r.baseUrl.trim();if(!t)throw Error("缺少 middle appid");if(!e)throw Error("缺少 middle baseUrl");return(o)=>n({url:o,appid:t,baseUrl:e})};export{c as createMiddleProxyUrlFactory};
@@ -0,0 +1,9 @@
1
+ export interface CreateMiddleProxyUrlParams {
2
+ url: string;
3
+ appid: string;
4
+ baseUrl: string;
5
+ }
6
+ export interface CreateMiddleProxyUrlFactoryParams {
7
+ appid: string;
8
+ baseUrl: string;
9
+ }
@@ -0,0 +1,2 @@
1
+ import type { CreateMiddleProxyUrlParams } from './types';
2
+ export declare const createMiddleProxyUrl: (params: CreateMiddleProxyUrlParams) => string;
@@ -0,0 +1,10 @@
1
+ export declare const middleUploadPath = "/api/file/upload";
2
+ export declare const middleUploadFileField = "file";
3
+ export declare const middleUploadTypeField = "type";
4
+ export declare const middleUploadBusinessField = "business";
5
+ export declare const middleUploadFileNameField = "fileName";
6
+ export declare const middleUploadMimeTypeField = "mimeType";
7
+ export declare const middleUploadSourceType: {
8
+ readonly File: "file";
9
+ readonly Url: "url";
10
+ };
@@ -0,0 +1,3 @@
1
+ import type { CreateMiddleUploadFileFactoryParams, CreateMiddleUploadPropsFactoryParams, CreateMiddleUploadPropsParams, MiddleUploadFileInput, MiddleUploadProps } from './types';
2
+ export declare const createMiddleUploadFileFactory: (params: CreateMiddleUploadFileFactoryParams) => (input: MiddleUploadFileInput) => Promise<import("./types").MiddleUploadedFile>;
3
+ export declare const createMiddleUploadPropsFactory: (params: CreateMiddleUploadPropsFactoryParams) => (option?: CreateMiddleUploadPropsParams) => MiddleUploadProps;
@@ -0,0 +1,2 @@
1
+ // @ts-nocheck
2
+ var c="/api/file/upload",a="file",t="type",U="business",m="fileName",f="mimeType",i={File:"file",Url:"url"};var x=(e)=>e.replace(/\/+$/,""),F=async(e)=>{if(typeof e.accessToken==="function")return e.accessToken();return e.accessToken};var s=(e,d,l)=>{if(l)e.append(d,l)},P=(e)=>{if(e.file instanceof Blob)return e.file;let d=e.file instanceof Uint8Array?e.file.slice().buffer:e.file;return new Blob([d],{type:e.mimeType})},n=(e)=>`${e.replace(/\/+$/,"")}${c}`,p=(e)=>e?{Authorization:`Bearer ${e}`}:void 0,M=(e,d)=>{let l={type:i.File};if(e.business)l.business=e.business;if(d.name)l.fileName=d.name;if(d.type)l.mimeType=d.type;return l},g=(e)=>{let d=new FormData;if(e.type===i.Url)d.append(t,i.Url),d.append(a,e.file);else d.append(t,i.File),d.append(a,P(e),e.fileName);return s(d,U,e.business),s(d,m,e.fileName),s(d,f,e.mimeType),d},T=(e)=>{if(!e||typeof e!=="object"||Array.isArray(e))return!1;let d=e;return typeof d.id==="string"&&typeof d.name==="string"&&typeof d.url==="string"},y=async(e)=>{let d=n(e.baseUrl),l=await F(e),o=await fetch(d,{body:g(e.input),method:"POST",signal:e.signal,headers:p(l)}),r=await o.json().catch(()=>null);if(!o.ok){let u=r&&"error"in r&&r.error?r.error:`中台文件上传失败:${o.status}`;throw Error(u)}if(!T(r))throw Error("中台文件上传响应格式错误");return r};var A=(e)=>{let d=e.baseUrl.trim();if(!d)throw Error("缺少 middle baseUrl");return(l)=>y({...e,baseUrl:d,input:l})},D=(e)=>{let d=e.baseUrl.trim();if(!d)throw Error("缺少 middle baseUrl");return(l={})=>({action:n(d),headers:p(e.accessToken),data:(o)=>M(l,o)})};export{D as createMiddleUploadPropsFactory,A as createMiddleUploadFileFactory};
@@ -0,0 +1,61 @@
1
+ import { middleUploadSourceType } from './consts';
2
+ type MaybePromise<T> = Promise<T> | T;
3
+ export type MiddleUploadBinaryFile = Blob | ArrayBuffer | Uint8Array;
4
+ export type MiddleUploadBinaryFileSource = {
5
+ type?: typeof middleUploadSourceType.File;
6
+ file: MiddleUploadBinaryFile;
7
+ };
8
+ export type MiddleUploadUrlSource = {
9
+ type: typeof middleUploadSourceType.Url;
10
+ file: string;
11
+ };
12
+ export type MiddleUploadFileSource = MiddleUploadBinaryFileSource | MiddleUploadUrlSource;
13
+ export interface MiddleUploadMetadata {
14
+ business?: string;
15
+ fileName?: string;
16
+ mimeType?: string;
17
+ }
18
+ export type MiddleUploadFileInput = MiddleUploadFileSource & MiddleUploadMetadata;
19
+ export interface CreateMiddleUploadFileFactoryParams {
20
+ baseUrl: string;
21
+ accessToken: string | (() => MaybePromise<string>);
22
+ }
23
+ export interface UploadMiddleFileParams extends CreateMiddleUploadFileFactoryParams {
24
+ input: MiddleUploadFileInput;
25
+ signal?: AbortSignal;
26
+ }
27
+ export interface CreateMiddleUploadPropsFactoryParams {
28
+ baseUrl: string;
29
+ accessToken?: string | null;
30
+ }
31
+ export interface CreateMiddleUploadPropsParams {
32
+ business?: string;
33
+ }
34
+ export interface MiddleUploadPropsFile {
35
+ name?: string;
36
+ type?: string;
37
+ }
38
+ export interface MiddleUploadProps {
39
+ action: string;
40
+ headers?: Record<string, string>;
41
+ data: (file: MiddleUploadPropsFile) => Record<string, string>;
42
+ }
43
+ export interface MiddleUploadedFile {
44
+ id: string;
45
+ name: string;
46
+ mimeType: string;
47
+ ext: string;
48
+ size: number;
49
+ hash: string;
50
+ objectKey: string;
51
+ provider: string;
52
+ status: string;
53
+ business: string | null;
54
+ applicationId: string;
55
+ creatorId: string;
56
+ url: string;
57
+ createdAt: string | Date;
58
+ updatedAt: string | Date;
59
+ isDeleted: boolean;
60
+ }
61
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { CreateMiddleUploadPropsParams, MiddleUploadedFile, MiddleUploadPropsFile, UploadMiddleFileParams } from './types';
2
+ export declare const createMiddleUploadUrl: (baseUrl: string) => string;
3
+ export declare const createMiddleUploadHeaders: (accessToken?: string | null) => Record<string, string> | undefined;
4
+ export declare const createMiddleUploadData: (params: CreateMiddleUploadPropsParams, file: MiddleUploadPropsFile) => Record<string, string>;
5
+ export declare const uploadMiddleFile: (params: UploadMiddleFileParams) => Promise<MiddleUploadedFile>;
package/package.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "@wzyjs/middle-sdk",
3
+ "version": "0.3.36",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "scripts": {
7
+ "build": "rm -rf dist && npm run build:js && npm run build:types",
8
+ "build:js": "node scripts/build.mjs",
9
+ "build:types": "tsc --emitDeclarationOnly",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "exports": {
16
+ "./api/node": {
17
+ "types": "./dist/api/node/index.d.ts",
18
+ "import": "./dist/api/node/index.js"
19
+ },
20
+ "./api/react": {
21
+ "types": "./dist/api/react/index.d.ts",
22
+ "import": "./dist/api/react/index.js"
23
+ },
24
+ "./login": {
25
+ "types": "./dist/login/index.d.ts",
26
+ "import": "./dist/login/index.js"
27
+ },
28
+ "./page": {
29
+ "types": "./dist/page/index.d.ts",
30
+ "import": "./dist/page/index.js"
31
+ },
32
+ "./proxy": {
33
+ "types": "./dist/proxy/index.d.ts",
34
+ "import": "./dist/proxy/index.js"
35
+ },
36
+ "./upload": {
37
+ "types": "./dist/upload/index.d.ts",
38
+ "import": "./dist/upload/index.js"
39
+ }
40
+ },
41
+ "dependencies": {
42
+ "@trpc/client": "^11.7.0",
43
+ "superjson": "^2.2.1"
44
+ },
45
+ "devDependencies": {
46
+ "@tanstack/react-query": "^5.50.0",
47
+ "@trpc/react-query": "^11.7.0",
48
+ "@trpc/server": "^11.7.0",
49
+ "@types/react": "^19.1.10",
50
+ "next": "^16.2.4",
51
+ "next-auth": "^4.24.11",
52
+ "react": "^19.1.0",
53
+ "typescript": "^5.9.3"
54
+ },
55
+ "peerDependencies": {
56
+ "@tanstack/react-query": ">=5",
57
+ "@trpc/client": ">=11",
58
+ "@trpc/react-query": ">=11",
59
+ "@trpc/server": ">=11",
60
+ "next": ">=15",
61
+ "next-auth": ">=4",
62
+ "react": ">=18"
63
+ },
64
+ "peerDependenciesMeta": {
65
+ "@tanstack/react-query": {
66
+ "optional": true
67
+ },
68
+ "@trpc/react-query": {
69
+ "optional": true
70
+ },
71
+ "@trpc/server": {
72
+ "optional": true
73
+ },
74
+ "next": {
75
+ "optional": true
76
+ },
77
+ "next-auth": {
78
+ "optional": true
79
+ },
80
+ "react": {
81
+ "optional": true
82
+ }
83
+ },
84
+ "publishConfig": {
85
+ "access": "public"
86
+ },
87
+ "gitHead": "7db0faf741993d729a9e1c0742f480f364816222"
88
+ }