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>
这个提交包含在:
XuqmGroup 2026-05-18 21:07:38 +08:00
父节点 7ef2d011a3
当前提交 cd28487dad
共有 6 个文件被更改,包括 241 次插入2 次删除

查看文件

@ -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 普通文件
查看文件

@ -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 普通文件
查看文件

@ -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 普通文件
查看文件

@ -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 普通文件
查看文件

@ -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'],