feat(private): add private deployment SDK module
Adds @xuqm/vue3-sdk/private entry point with JSON-based initialization, feature gating, and error codes for private deployment scenarios. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
父节点
7ef2d011a3
当前提交
cd28487dad
@ -14,6 +14,11 @@
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.es.js",
|
||||
"require": "./dist/index.cjs.js"
|
||||
},
|
||||
"./private": {
|
||||
"types": "./dist/private/index.d.ts",
|
||||
"import": "./dist/private/index.es.js",
|
||||
"require": "./dist/private/index.cjs.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
|
||||
25
package.private.json
普通文件
25
package.private.json
普通文件
@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@xuqm-private/vue3-sdk",
|
||||
"version": "0.2.3",
|
||||
"description": "XuqmGroup Vue3 Private SDK — for private deployment environments",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"registry": "https://nexus.xuqinmin.com/repository/npm-hosted/"
|
||||
},
|
||||
"main": "dist/private/index.cjs.js",
|
||||
"module": "dist/private/index.es.js",
|
||||
"types": "dist/private/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/private/index.d.ts",
|
||||
"import": "./dist/private/index.es.js",
|
||||
"require": "./dist/private/index.cjs.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist/private"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"vue": "^3.5.0"
|
||||
}
|
||||
}
|
||||
15
src/private/index.ts
普通文件
15
src/private/index.ts
普通文件
@ -0,0 +1,15 @@
|
||||
export {
|
||||
initFromConfig,
|
||||
initFromFile,
|
||||
reloadConfig,
|
||||
requireFeature,
|
||||
getPrivateConfig,
|
||||
isFeatureEnabled,
|
||||
setToken,
|
||||
setUserId,
|
||||
getToken,
|
||||
getUserId,
|
||||
} from './sdk'
|
||||
|
||||
export type { PrivateSdkConfig, PrivateSdkFeatures, PrivateSdkState, PrivateErrorCode } from './types'
|
||||
export { PrivateSdkError } from './types'
|
||||
138
src/private/sdk.ts
普通文件
138
src/private/sdk.ts
普通文件
@ -0,0 +1,138 @@
|
||||
import type { PrivateSdkConfig, PrivateSdkState } from './types'
|
||||
import { PrivateSdkError } from './types'
|
||||
import { configureHttp } from '../core/http'
|
||||
|
||||
const SUPPORTED_SCHEMA_VERSIONS = [1]
|
||||
|
||||
const state: PrivateSdkState = {
|
||||
config: null,
|
||||
token: null,
|
||||
userId: null,
|
||||
initialized: false,
|
||||
}
|
||||
|
||||
function validateConfig(cfg: PrivateSdkConfig): void {
|
||||
if (!SUPPORTED_SCHEMA_VERSIONS.includes(cfg.schemaVersion)) {
|
||||
throw new PrivateSdkError(
|
||||
'XUQM_PRIVATE_1003',
|
||||
`Unsupported schemaVersion: ${cfg.schemaVersion}. Supported: ${SUPPORTED_SCHEMA_VERSIONS.join(', ')}`,
|
||||
)
|
||||
}
|
||||
if (cfg.deployment !== 'PRIVATE') {
|
||||
throw new PrivateSdkError('XUQM_PRIVATE_1002', 'deployment must be "PRIVATE"')
|
||||
}
|
||||
if (!cfg.appKey) {
|
||||
throw new PrivateSdkError('XUQM_PRIVATE_1002', 'appKey is required')
|
||||
}
|
||||
if (!cfg.controlBaseUrl) {
|
||||
throw new PrivateSdkError('XUQM_PRIVATE_1002', 'controlBaseUrl is required')
|
||||
}
|
||||
validateUrl('controlBaseUrl', cfg.controlBaseUrl, false)
|
||||
if (cfg.features.im) {
|
||||
if (!cfg.imApiBaseUrl) throw new PrivateSdkError('XUQM_PRIVATE_1002', 'imApiBaseUrl is required when im is enabled')
|
||||
if (!cfg.imWsUrl) throw new PrivateSdkError('XUQM_PRIVATE_1002', 'imWsUrl is required when im is enabled')
|
||||
validateUrl('imApiBaseUrl', cfg.imApiBaseUrl, false)
|
||||
validateWsUrl('imWsUrl', cfg.imWsUrl)
|
||||
}
|
||||
if (cfg.features.push && !cfg.pushBaseUrl) {
|
||||
throw new PrivateSdkError('XUQM_PRIVATE_1002', 'pushBaseUrl is required when push is enabled')
|
||||
}
|
||||
if (cfg.features.update && !cfg.updateBaseUrl) {
|
||||
throw new PrivateSdkError('XUQM_PRIVATE_1002', 'updateBaseUrl is required when update is enabled')
|
||||
}
|
||||
if (cfg.features.license && !cfg.licenseBaseUrl) {
|
||||
throw new PrivateSdkError('XUQM_PRIVATE_1002', 'licenseBaseUrl is required when license is enabled')
|
||||
}
|
||||
}
|
||||
|
||||
function validateUrl(field: string, url: string, optional: boolean): void {
|
||||
if (!url) {
|
||||
if (!optional) throw new PrivateSdkError('XUQM_PRIVATE_1004', `${field} is required`)
|
||||
return
|
||||
}
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
throw new PrivateSdkError('XUQM_PRIVATE_1004', `${field} must start with http:// or https://`)
|
||||
}
|
||||
}
|
||||
|
||||
function validateWsUrl(field: string, url: string): void {
|
||||
if (!url.startsWith('ws://') && !url.startsWith('wss://')) {
|
||||
throw new PrivateSdkError('XUQM_PRIVATE_1004', `${field} must start with ws:// or wss://`)
|
||||
}
|
||||
}
|
||||
|
||||
function resolveActiveConfig(cfg: PrivateSdkConfig): PrivateSdkConfig {
|
||||
if (!cfg.activeProfile || !cfg.profiles) return cfg
|
||||
const profile = cfg.profiles[cfg.activeProfile]
|
||||
if (!profile) return cfg
|
||||
return { ...cfg, ...profile, activeProfile: cfg.activeProfile, profiles: cfg.profiles }
|
||||
}
|
||||
|
||||
function applyConfig(cfg: PrivateSdkConfig): void {
|
||||
const resolved = resolveActiveConfig(cfg)
|
||||
validateConfig(resolved)
|
||||
state.config = resolved
|
||||
state.initialized = true
|
||||
// wire up the shared http client with the control base URL (no default fallback)
|
||||
configureHttp(normalizeUrl(resolved.controlBaseUrl), () => state.token)
|
||||
}
|
||||
|
||||
function normalizeUrl(url: string): string {
|
||||
return url.endsWith('/') ? url.slice(0, -1) : url
|
||||
}
|
||||
|
||||
export function initFromConfig(cfg: PrivateSdkConfig): void {
|
||||
applyConfig(cfg)
|
||||
}
|
||||
|
||||
export async function initFromFile(url: string): Promise<void> {
|
||||
const res = await fetch(url)
|
||||
if (!res.ok) throw new PrivateSdkError('XUQM_PRIVATE_1002', `Failed to load config from ${url}: ${res.status}`)
|
||||
const cfg = (await res.json()) as PrivateSdkConfig
|
||||
applyConfig(cfg)
|
||||
}
|
||||
|
||||
export async function reloadConfig(url: string): Promise<void> {
|
||||
await initFromFile(url)
|
||||
}
|
||||
|
||||
export function requireFeature(
|
||||
feature: keyof PrivateSdkConfig['features'],
|
||||
): void {
|
||||
if (!state.config) {
|
||||
throw new PrivateSdkError('XUQM_PRIVATE_1001', 'Private SDK not initialized. Call initFromConfig() or initFromFile() first.')
|
||||
}
|
||||
if (!state.config.features[feature]) {
|
||||
throw new PrivateSdkError(
|
||||
'XUQM_PRIVATE_2001',
|
||||
`Feature "${feature}" is not enabled in this deployment. Contact your administrator to enable it.`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function getPrivateConfig(): PrivateSdkConfig {
|
||||
if (!state.config) {
|
||||
throw new PrivateSdkError('XUQM_PRIVATE_1001', 'Private SDK not initialized.')
|
||||
}
|
||||
return state.config
|
||||
}
|
||||
|
||||
export function isFeatureEnabled(feature: keyof PrivateSdkConfig['features']): boolean {
|
||||
return !!state.config?.features[feature]
|
||||
}
|
||||
|
||||
export function setToken(token: string | null): void {
|
||||
state.token = token
|
||||
}
|
||||
|
||||
export function setUserId(userId: string | null): void {
|
||||
state.userId = userId
|
||||
}
|
||||
|
||||
export function getToken(): string | null {
|
||||
return state.token
|
||||
}
|
||||
|
||||
export function getUserId(): string | null {
|
||||
return state.userId
|
||||
}
|
||||
53
src/private/types.ts
普通文件
53
src/private/types.ts
普通文件
@ -0,0 +1,53 @@
|
||||
export interface PrivateSdkFeatures {
|
||||
file?: boolean
|
||||
im?: boolean
|
||||
push?: boolean
|
||||
update?: boolean
|
||||
license?: boolean
|
||||
}
|
||||
|
||||
export interface PrivateSdkConfig {
|
||||
schemaVersion: number
|
||||
configVersion?: string
|
||||
deployment: 'PRIVATE'
|
||||
appKey: string
|
||||
controlBaseUrl: string
|
||||
imApiBaseUrl?: string
|
||||
imWsUrl?: string
|
||||
pushBaseUrl?: string
|
||||
updateBaseUrl?: string
|
||||
fileBaseUrl?: string
|
||||
licenseBaseUrl?: string
|
||||
docsBaseUrl?: string
|
||||
features: PrivateSdkFeatures
|
||||
connectTimeoutMs?: number
|
||||
readTimeoutMs?: number
|
||||
logLevel?: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR'
|
||||
activeProfile?: string
|
||||
profiles?: Record<string, Partial<PrivateSdkConfig>>
|
||||
}
|
||||
|
||||
export interface PrivateSdkState {
|
||||
config: PrivateSdkConfig | null
|
||||
token: string | null
|
||||
userId: string | null
|
||||
initialized: boolean
|
||||
}
|
||||
|
||||
export type PrivateErrorCode =
|
||||
| 'XUQM_PRIVATE_1001' // config not initialized / JSON not found
|
||||
| 'XUQM_PRIVATE_1002' // invalid config: missing required field or parse error
|
||||
| 'XUQM_PRIVATE_1003' // schema version incompatible
|
||||
| 'XUQM_PRIVATE_1004' // invalid URL format
|
||||
| 'XUQM_PRIVATE_2001' // feature not enabled in deployment
|
||||
| 'XUQM_PRIVATE_2004' // module not initialized
|
||||
|
||||
export class PrivateSdkError extends Error {
|
||||
constructor(
|
||||
public readonly code: PrivateErrorCode,
|
||||
message: string,
|
||||
) {
|
||||
super(`[${code}] ${message}`)
|
||||
this.name = 'PrivateSdkError'
|
||||
}
|
||||
}
|
||||
@ -10,10 +10,13 @@ export default defineConfig({
|
||||
],
|
||||
build: {
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'src/index.ts'),
|
||||
entry: {
|
||||
index: resolve(__dirname, 'src/index.ts'),
|
||||
'private/index': resolve(__dirname, 'src/private/index.ts'),
|
||||
},
|
||||
name: 'XuqmVue3SDK',
|
||||
formats: ['es', 'cjs'],
|
||||
fileName: (format) => `index.${format}.js`,
|
||||
fileName: (format, entryName) => `${entryName}.${format}.js`,
|
||||
},
|
||||
rollupOptions: {
|
||||
external: ['vue'],
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户