@spatialwalk/avatarkit 1.0.0-beta.10 → 1.0.0-beta.101
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/CHANGELOG.md +771 -4
- package/README.md +676 -365
- package/dist/StreamingAudioPlayer-BULgPjpe.js +643 -0
- package/dist/avatar_core_wasm-CQbUl6zN.js +2696 -0
- package/dist/avatar_core_wasm-bd762669.wasm +0 -0
- package/dist/core/Avatar.d.ts +5 -7
- package/dist/core/AvatarController.d.ts +99 -60
- package/dist/core/AvatarManager.d.ts +32 -12
- package/dist/core/AvatarSDK.d.ts +58 -0
- package/dist/core/AvatarView.d.ts +136 -128
- package/dist/index-C0A1HA8M.js +18427 -0
- package/dist/index.d.ts +2 -4
- package/dist/index.js +17 -17
- package/dist/next.d.ts +2 -0
- package/dist/performance/FrameRateMonitor.d.ts +85 -0
- package/dist/types/character-settings.d.ts +7 -1
- package/dist/types/character.d.ts +42 -16
- package/dist/types/index.d.ts +165 -45
- package/dist/vite.d.ts +19 -0
- package/next.d.ts +3 -0
- package/next.js +187 -0
- package/package.json +38 -8
- package/vite.d.ts +20 -0
- package/vite.js +126 -0
- package/dist/StreamingAudioPlayer-Bq2-bQiT.js +0 -319
- package/dist/StreamingAudioPlayer-Bq2-bQiT.js.map +0 -1
- package/dist/animation/AnimationWebSocketClient.d.ts +0 -50
- package/dist/animation/AnimationWebSocketClient.d.ts.map +0 -1
- package/dist/animation/utils/eventEmitter.d.ts +0 -13
- package/dist/animation/utils/eventEmitter.d.ts.map +0 -1
- package/dist/animation/utils/flameConverter.d.ts +0 -26
- package/dist/animation/utils/flameConverter.d.ts.map +0 -1
- package/dist/audio/AnimationPlayer.d.ts +0 -57
- package/dist/audio/AnimationPlayer.d.ts.map +0 -1
- package/dist/audio/StreamingAudioPlayer.d.ts +0 -123
- package/dist/audio/StreamingAudioPlayer.d.ts.map +0 -1
- package/dist/avatar_core_wasm-D4eEi7Eh.js +0 -1666
- package/dist/avatar_core_wasm-D4eEi7Eh.js.map +0 -1
- package/dist/avatar_core_wasm.wasm +0 -0
- package/dist/config/app-config.d.ts +0 -44
- package/dist/config/app-config.d.ts.map +0 -1
- package/dist/config/constants.d.ts +0 -29
- package/dist/config/constants.d.ts.map +0 -1
- package/dist/config/sdk-config-loader.d.ts +0 -12
- package/dist/config/sdk-config-loader.d.ts.map +0 -1
- package/dist/core/Avatar.d.ts.map +0 -1
- package/dist/core/AvatarController.d.ts.map +0 -1
- package/dist/core/AvatarDownloader.d.ts +0 -95
- package/dist/core/AvatarDownloader.d.ts.map +0 -1
- package/dist/core/AvatarKit.d.ts +0 -48
- package/dist/core/AvatarKit.d.ts.map +0 -1
- package/dist/core/AvatarManager.d.ts.map +0 -1
- package/dist/core/AvatarView.d.ts.map +0 -1
- package/dist/core/NetworkLayer.d.ts +0 -59
- package/dist/core/NetworkLayer.d.ts.map +0 -1
- package/dist/generated/driveningress/v1/driveningress.d.ts +0 -80
- package/dist/generated/driveningress/v1/driveningress.d.ts.map +0 -1
- package/dist/generated/driveningress/v2/driveningress.d.ts +0 -81
- package/dist/generated/driveningress/v2/driveningress.d.ts.map +0 -1
- package/dist/generated/google/protobuf/struct.d.ts +0 -108
- package/dist/generated/google/protobuf/struct.d.ts.map +0 -1
- package/dist/generated/google/protobuf/timestamp.d.ts +0 -129
- package/dist/generated/google/protobuf/timestamp.d.ts.map +0 -1
- package/dist/index-bQnEVIkT.js +0 -5999
- package/dist/index-bQnEVIkT.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/renderer/RenderSystem.d.ts +0 -79
- package/dist/renderer/RenderSystem.d.ts.map +0 -1
- package/dist/renderer/covariance.d.ts +0 -13
- package/dist/renderer/covariance.d.ts.map +0 -1
- package/dist/renderer/renderer.d.ts +0 -8
- package/dist/renderer/renderer.d.ts.map +0 -1
- package/dist/renderer/sortSplats.d.ts +0 -12
- package/dist/renderer/sortSplats.d.ts.map +0 -1
- package/dist/renderer/webgl/reorderData.d.ts +0 -14
- package/dist/renderer/webgl/reorderData.d.ts.map +0 -1
- package/dist/renderer/webgl/webglRenderer.d.ts +0 -66
- package/dist/renderer/webgl/webglRenderer.d.ts.map +0 -1
- package/dist/renderer/webgpu/webgpuRenderer.d.ts +0 -54
- package/dist/renderer/webgpu/webgpuRenderer.d.ts.map +0 -1
- package/dist/types/character-settings.d.ts.map +0 -1
- package/dist/types/character.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/utils/animation-interpolation.d.ts +0 -17
- package/dist/utils/animation-interpolation.d.ts.map +0 -1
- package/dist/utils/cls-tracker.d.ts +0 -17
- package/dist/utils/cls-tracker.d.ts.map +0 -1
- package/dist/utils/error-utils.d.ts +0 -27
- package/dist/utils/error-utils.d.ts.map +0 -1
- package/dist/utils/logger.d.ts +0 -35
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/reqId.d.ts +0 -20
- package/dist/utils/reqId.d.ts.map +0 -1
- package/dist/wasm/avatarCoreAdapter.d.ts +0 -188
- package/dist/wasm/avatarCoreAdapter.d.ts.map +0 -1
- package/dist/wasm/avatarCoreMemory.d.ts +0 -141
- package/dist/wasm/avatarCoreMemory.d.ts.map +0 -1
package/next.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js plugin for @spatialwalk/avatarkit
|
|
3
|
+
* Handles WASM file configuration for both webpack and Turbopack (Next.js 15+/16+)
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* import { withAvatarkit } from '@spatialwalk/avatarkit/next'
|
|
7
|
+
* export default withAvatarkit({ ...your next config... })
|
|
8
|
+
*/
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
import { writeFileSync, readFileSync, readdirSync, mkdirSync, copyFileSync, existsSync, } from 'fs';
|
|
12
|
+
// ── Locate SDK directory ──────────────────────────────────────────────
|
|
13
|
+
// Walk up from this file (inside the SDK package) to find the package root.
|
|
14
|
+
// This is reliable regardless of process.cwd(), monorepos, or hoisted deps.
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = dirname(__filename);
|
|
17
|
+
// Since this file IS inside the SDK package (at package root), __dirname is the SDK dir.
|
|
18
|
+
const sdkDir = __dirname;
|
|
19
|
+
const sdkDistDir = join(sdkDir, 'dist');
|
|
20
|
+
// ── Embedded webpack loader ───────────────────────────────────────────
|
|
21
|
+
// Fixes Emscripten's scriptDirectory: bundlers replace import.meta.url with
|
|
22
|
+
// a build-time path that browsers can't use to locate WASM files.
|
|
23
|
+
// This loader replaces it with the correct public path.
|
|
24
|
+
function createLoaderCode(wasmPublicPath) {
|
|
25
|
+
return `module.exports = function(source) {
|
|
26
|
+
// Strategy 1: Replace assignment only (simpler, more robust)
|
|
27
|
+
var pattern1 = /scriptDirectory\\s*=\\s*new\\s+URL\\(\\s*"\\."\\s*,\\s*_scriptName\\s*\\)\\.href\\s*;/;
|
|
28
|
+
var result = source.replace(pattern1, 'scriptDirectory = "${wasmPublicPath}";');
|
|
29
|
+
if (result !== source) return result;
|
|
30
|
+
|
|
31
|
+
// Strategy 2: Full try/catch block (handles different catch syntax)
|
|
32
|
+
var pattern2 = /try\\s*\\{\\s*scriptDirectory\\s*=\\s*new\\s+URL\\(\\s*"\\."\\s*,\\s*_scriptName\\s*\\)\\.href\\s*;?\\s*\\}\\s*catch\\s*(\\([^)]*\\))?\\s*\\{\\s*\\}/;
|
|
33
|
+
result = source.replace(pattern2, 'scriptDirectory = "${wasmPublicPath}";');
|
|
34
|
+
if (result !== source) return result;
|
|
35
|
+
|
|
36
|
+
console.warn('[avatarkit] WARNING: scriptDirectory pattern not matched in', this.resourcePath);
|
|
37
|
+
return source;
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
}
|
|
41
|
+
function ensureLoader(wasmPublicPath) {
|
|
42
|
+
const cacheDir = join(sdkDir, '.cache');
|
|
43
|
+
const loaderPath = join(cacheDir, 'wasm-script-dir-loader.cjs');
|
|
44
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
45
|
+
writeFileSync(loaderPath, createLoaderCode(wasmPublicPath));
|
|
46
|
+
return loaderPath;
|
|
47
|
+
}
|
|
48
|
+
// ── Copy WASM files to public directory ───────────────────────────────
|
|
49
|
+
// Works for both webpack and Turbopack: public/ files are served by Next.js
|
|
50
|
+
function copyWasmToPublic(projectDir) {
|
|
51
|
+
const publicWasmDir = join(projectDir, 'public', '_avatarkit');
|
|
52
|
+
mkdirSync(publicWasmDir, { recursive: true });
|
|
53
|
+
try {
|
|
54
|
+
const files = readdirSync(sdkDistDir);
|
|
55
|
+
for (const file of files) {
|
|
56
|
+
if (file.startsWith('avatar_core_wasm') && file.endsWith('.wasm')) {
|
|
57
|
+
const src = join(sdkDistDir, file);
|
|
58
|
+
const dest = join(publicWasmDir, file);
|
|
59
|
+
copyFileSync(src, dest);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
console.warn('[avatarkit] Failed to copy WASM files:', err.message);
|
|
65
|
+
console.warn('[avatarkit] SDK dist dir:', sdkDistDir);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// ── Webpack plugin: copy WASM to build output ─────────────────────────
|
|
69
|
+
// Emits WASM files into .next/static/chunks/ during webpack compilation.
|
|
70
|
+
// This is needed in addition to public/ for standalone Docker deployments
|
|
71
|
+
// where .next/static/ is copied but public/ may not be.
|
|
72
|
+
class CopyWasmPlugin {
|
|
73
|
+
apply(compiler) {
|
|
74
|
+
compiler.hooks.thisCompilation.tap('AvatarkitCopyWasm', (compilation) => {
|
|
75
|
+
compilation.hooks.processAssets.tap({
|
|
76
|
+
name: 'AvatarkitCopyWasm',
|
|
77
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
|
|
78
|
+
}, () => {
|
|
79
|
+
try {
|
|
80
|
+
const files = readdirSync(sdkDistDir);
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
if (file.startsWith('avatar_core_wasm') && file.endsWith('.wasm')) {
|
|
83
|
+
const content = readFileSync(join(sdkDistDir, file));
|
|
84
|
+
compilation.emitAsset(`static/chunks/${file}`, new compiler.webpack.sources.RawSource(content));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
console.warn('[avatarkit] CopyWasmPlugin error:', err.message);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// ── Detect project root from next.config location ─────────────────────
|
|
96
|
+
// next.config.mjs is always at the project root. Since this module is imported
|
|
97
|
+
// from next.config.mjs, we find the project root by walking up from import.meta.url
|
|
98
|
+
// of the calling module. However, we can't access the caller's import.meta.url,
|
|
99
|
+
// so we search for the nearest next.config.* file from the SDK location upwards.
|
|
100
|
+
function findProjectRoot() {
|
|
101
|
+
let dir = process.cwd(); // fallback, but also check for next.config
|
|
102
|
+
const configNames = [
|
|
103
|
+
'next.config.mjs',
|
|
104
|
+
'next.config.js',
|
|
105
|
+
'next.config.ts',
|
|
106
|
+
'next.config.cjs',
|
|
107
|
+
];
|
|
108
|
+
// Verify cwd has a next.config file
|
|
109
|
+
for (const name of configNames) {
|
|
110
|
+
if (existsSync(join(dir, name))) {
|
|
111
|
+
return dir;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Fallback: search from SDK location upward
|
|
115
|
+
let searchDir = sdkDir;
|
|
116
|
+
while (searchDir !== dirname(searchDir)) {
|
|
117
|
+
for (const name of configNames) {
|
|
118
|
+
if (existsSync(join(searchDir, name))) {
|
|
119
|
+
return searchDir;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
searchDir = dirname(searchDir);
|
|
123
|
+
}
|
|
124
|
+
return process.cwd();
|
|
125
|
+
}
|
|
126
|
+
// ── Main plugin ───────────────────────────────────────────────────────
|
|
127
|
+
export function withAvatarkit(nextConfig = {}) {
|
|
128
|
+
const basePath = nextConfig.basePath || '';
|
|
129
|
+
const wasmPublicPath = `${basePath}/_avatarkit/`;
|
|
130
|
+
const loaderPath = ensureLoader(wasmPublicPath);
|
|
131
|
+
const projectRoot = findProjectRoot();
|
|
132
|
+
// Copy WASM files to public/_avatarkit/ (works for both webpack & Turbopack)
|
|
133
|
+
copyWasmToPublic(projectRoot);
|
|
134
|
+
return {
|
|
135
|
+
...nextConfig,
|
|
136
|
+
// ── Turbopack configuration (Next.js 15+/16+) ──
|
|
137
|
+
turbopack: {
|
|
138
|
+
...nextConfig.turbopack,
|
|
139
|
+
rules: {
|
|
140
|
+
...nextConfig.turbopack?.rules,
|
|
141
|
+
'**/avatar_core_wasm*.js': {
|
|
142
|
+
loaders: [loaderPath],
|
|
143
|
+
as: '*.js',
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
// ── Webpack configuration ──
|
|
148
|
+
webpack: (config, context) => {
|
|
149
|
+
// 1. Fix: Next.js sets module.generator.asset.filename which breaks asset/inline
|
|
150
|
+
if (config.module.generator?.asset?.filename) {
|
|
151
|
+
const filename = config.module.generator.asset.filename;
|
|
152
|
+
delete config.module.generator.asset.filename;
|
|
153
|
+
config.module.generator['asset/resource'] = {
|
|
154
|
+
...config.module.generator['asset/resource'],
|
|
155
|
+
filename,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
// 2. Fix: Emscripten scriptDirectory derived from import.meta.url
|
|
159
|
+
config.module.rules.push({
|
|
160
|
+
test: /avatar_core_wasm.*\.js$/,
|
|
161
|
+
enforce: 'pre',
|
|
162
|
+
use: [{ loader: loaderPath }],
|
|
163
|
+
});
|
|
164
|
+
// 3. Copy WASM files to static/chunks/ (client build only, for standalone)
|
|
165
|
+
if (!context.isServer) {
|
|
166
|
+
config.plugins.push(new CopyWasmPlugin());
|
|
167
|
+
}
|
|
168
|
+
// Chain with user's webpack config
|
|
169
|
+
if (typeof nextConfig.webpack === 'function') {
|
|
170
|
+
return nextConfig.webpack(config, context);
|
|
171
|
+
}
|
|
172
|
+
return config;
|
|
173
|
+
},
|
|
174
|
+
// ── Headers: ensure WASM content-type ──
|
|
175
|
+
async headers() {
|
|
176
|
+
const userHeaders = typeof nextConfig.headers === 'function' ? await nextConfig.headers() : [];
|
|
177
|
+
return [
|
|
178
|
+
...userHeaders,
|
|
179
|
+
{
|
|
180
|
+
source: '/_avatarkit/:path*.wasm',
|
|
181
|
+
headers: [{ key: 'Content-Type', value: 'application/wasm' }],
|
|
182
|
+
},
|
|
183
|
+
];
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
export default withAvatarkit;
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spatialwalk/avatarkit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0-beta.
|
|
4
|
+
"version": "1.0.0-beta.101",
|
|
5
5
|
"packageManager": "pnpm@10.18.2",
|
|
6
|
-
"description": "
|
|
7
|
-
"author": "
|
|
6
|
+
"description": "AvatarKit SDK - 3D Gaussian Splatting Avatar Rendering SDK",
|
|
7
|
+
"author": "AvatarKit Team",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"keywords": [
|
|
10
10
|
"avatar",
|
|
@@ -19,6 +19,14 @@
|
|
|
19
19
|
"types": "./dist/index.d.ts",
|
|
20
20
|
"import": "./dist/index.js"
|
|
21
21
|
},
|
|
22
|
+
"./vite": {
|
|
23
|
+
"types": "./vite.d.ts",
|
|
24
|
+
"import": "./vite.js"
|
|
25
|
+
},
|
|
26
|
+
"./next": {
|
|
27
|
+
"types": "./next.d.ts",
|
|
28
|
+
"import": "./next.js"
|
|
29
|
+
},
|
|
22
30
|
"./*": {
|
|
23
31
|
"types": "./dist/*.d.ts",
|
|
24
32
|
"import": "./dist/*.js"
|
|
@@ -30,28 +38,50 @@
|
|
|
30
38
|
"files": [
|
|
31
39
|
"README.md",
|
|
32
40
|
"CHANGELOG.md",
|
|
33
|
-
"dist"
|
|
41
|
+
"dist",
|
|
42
|
+
"vite.js",
|
|
43
|
+
"vite.d.ts",
|
|
44
|
+
"next.js",
|
|
45
|
+
"next.d.ts"
|
|
34
46
|
],
|
|
35
47
|
"scripts": {
|
|
36
|
-
"build": "SDK_BUILD=true vite build --mode library",
|
|
48
|
+
"build": "SDK_BUILD=true vite build --mode library && npm run build:vite-plugin && npm run build:next-plugin",
|
|
49
|
+
"build:vite-plugin": "tsc vite.ts --outDir . --module esnext --target es2020 --moduleResolution bundler --esModuleInterop --skipLibCheck --declaration --declarationMap",
|
|
50
|
+
"build:next-plugin": "tsc next.ts --outDir . --module esnext --target es2020 --moduleResolution bundler --esModuleInterop --skipLibCheck --declaration --declarationMap",
|
|
37
51
|
"dev": "vite build --mode library --watch",
|
|
52
|
+
"demo": "vite --config demo/vite.config.mjs",
|
|
53
|
+
"demo:benchmark": "vite --config benchmark-demo/vite.config.mjs",
|
|
54
|
+
"test:integration": "vite --config tests/integration-runner/vite.config.mjs",
|
|
38
55
|
"clean": "rm -rf dist",
|
|
39
56
|
"typecheck": "tsc --noEmit",
|
|
40
57
|
"test": "cd tests && pnpm test",
|
|
41
58
|
"test:watch": "cd tests && pnpm run test:watch",
|
|
42
|
-
"test:e2e": "cd tests && pnpm run test:e2e"
|
|
59
|
+
"test:e2e": "cd tests && pnpm run test:e2e",
|
|
60
|
+
"test:perf": "cd tests && pnpm run test:perf"
|
|
43
61
|
},
|
|
44
62
|
"peerDependencies": {
|
|
45
|
-
"@webgpu/types": "*"
|
|
63
|
+
"@webgpu/types": "*",
|
|
64
|
+
"next": ">=13.0.0",
|
|
65
|
+
"vite": "^5.0.0"
|
|
66
|
+
},
|
|
67
|
+
"peerDependenciesMeta": {
|
|
68
|
+
"vite": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"next": {
|
|
72
|
+
"optional": true
|
|
73
|
+
}
|
|
46
74
|
},
|
|
47
75
|
"dependencies": {
|
|
48
76
|
"@bufbuild/protobuf": "^2.10.0",
|
|
49
77
|
"@guiiai/logg": "^1.2.4",
|
|
50
|
-
"nanoid": "^5.1.6"
|
|
78
|
+
"nanoid": "^5.1.6",
|
|
79
|
+
"posthog-js": "^1.310.1"
|
|
51
80
|
},
|
|
52
81
|
"devDependencies": {
|
|
53
82
|
"@types/node": "^20.11.30",
|
|
54
83
|
"@webgpu/types": "^0.1.65",
|
|
84
|
+
"tsx": "^4.20.6",
|
|
55
85
|
"typescript": "^5.0.0",
|
|
56
86
|
"vite": "^5.0.0",
|
|
57
87
|
"vite-plugin-dts": "^4.5.4"
|
package/vite.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
/**
|
|
3
|
+
* Vite plugin for @spatialwalk/avatarkit
|
|
4
|
+
* Automatically handles WASM file configuration for development and production builds
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { defineConfig } from 'vite'
|
|
9
|
+
* import { avatarkitVitePlugin } from '@spatialwalk/avatarkit/vite'
|
|
10
|
+
*
|
|
11
|
+
* export default defineConfig({
|
|
12
|
+
* plugins: [
|
|
13
|
+
* avatarkitVitePlugin()
|
|
14
|
+
* ]
|
|
15
|
+
* })
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function avatarkitVitePlugin(): Plugin;
|
|
19
|
+
export default avatarkitVitePlugin;
|
|
20
|
+
//# sourceMappingURL=vite.d.ts.map
|
package/vite.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { copyFileSync, existsSync, writeFileSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Vite plugin for @spatialwalk/avatarkit
|
|
5
|
+
* Automatically handles WASM file configuration for development and production builds
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { defineConfig } from 'vite'
|
|
10
|
+
* import { avatarkitVitePlugin } from '@spatialwalk/avatarkit/vite'
|
|
11
|
+
*
|
|
12
|
+
* export default defineConfig({
|
|
13
|
+
* plugins: [
|
|
14
|
+
* avatarkitVitePlugin()
|
|
15
|
+
* ]
|
|
16
|
+
* })
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function avatarkitVitePlugin() {
|
|
20
|
+
let rootDir;
|
|
21
|
+
return {
|
|
22
|
+
name: 'avatarkit-wasm',
|
|
23
|
+
// 保存项目根目录
|
|
24
|
+
configResolved(config) {
|
|
25
|
+
rootDir = config.root;
|
|
26
|
+
},
|
|
27
|
+
// 开发服务器 MIME 类型配置
|
|
28
|
+
configureServer(server) {
|
|
29
|
+
server.middlewares.use((req, res, next) => {
|
|
30
|
+
if (req.url?.endsWith('.wasm')) {
|
|
31
|
+
res.setHeader('Content-Type', 'application/wasm');
|
|
32
|
+
}
|
|
33
|
+
next();
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
// 构建时自动复制 WASM 文件和生成 headers
|
|
37
|
+
closeBundle() {
|
|
38
|
+
if (!rootDir)
|
|
39
|
+
return;
|
|
40
|
+
const wasmSourceDir = join(rootDir, 'node_modules/@spatialwalk/avatarkit/dist');
|
|
41
|
+
// 先查找并读取 JS glue 文件,提取它引用的 WASM 文件名
|
|
42
|
+
let wasmFileName = null;
|
|
43
|
+
if (existsSync(wasmSourceDir)) {
|
|
44
|
+
const files = readdirSync(wasmSourceDir);
|
|
45
|
+
const wasmJsFile = files.find((f) => f.startsWith('avatar_core_wasm') && f.endsWith('.js'));
|
|
46
|
+
if (wasmJsFile) {
|
|
47
|
+
const wasmJsSource = join(wasmSourceDir, wasmJsFile);
|
|
48
|
+
try {
|
|
49
|
+
const jsContent = readFileSync(wasmJsSource, 'utf-8');
|
|
50
|
+
// 从 JS 文件中提取 WASM 文件名
|
|
51
|
+
// 匹配 avatar_core_wasm-{hash}.wasm 或 avatar_core_wasm.wasm
|
|
52
|
+
// 使用更精确的正则,匹配带引号或不带引号的情况
|
|
53
|
+
const wasmMatch = jsContent.match(/["'`]?avatar_core_wasm[-\w]*\.wasm["'`]?/g);
|
|
54
|
+
if (wasmMatch && wasmMatch.length > 0) {
|
|
55
|
+
// 取第一个匹配,去掉引号
|
|
56
|
+
wasmFileName = wasmMatch[0].replace(/["'`]/g, '');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.warn('⚠️ [avatarkit] Could not read JS glue file:', error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// 如果从 JS 文件中找到了 WASM 文件名,使用它;否则回退到默认名称
|
|
65
|
+
const targetWasmName = wasmFileName || 'avatar_core_wasm.wasm';
|
|
66
|
+
const wasmSource = join(wasmSourceDir, targetWasmName);
|
|
67
|
+
const wasmDest = join(rootDir, `dist/assets/${targetWasmName}`);
|
|
68
|
+
const wasmJsDest = join(rootDir, 'dist/assets/avatar_core_wasm.js');
|
|
69
|
+
const headersDest = join(rootDir, 'dist/_headers');
|
|
70
|
+
// 复制 WASM 文件
|
|
71
|
+
if (existsSync(wasmSource)) {
|
|
72
|
+
copyFileSync(wasmSource, wasmDest);
|
|
73
|
+
console.log(`✅ [avatarkit] Copied WASM file to dist/assets/${targetWasmName}`);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.warn(`⚠️ [avatarkit] WASM file not found: ${wasmSource}`);
|
|
77
|
+
if (wasmFileName) {
|
|
78
|
+
console.warn(` Expected file: ${targetWasmName} (extracted from JS glue file)`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// 复制 WASM JS glue 文件
|
|
82
|
+
if (existsSync(wasmSourceDir)) {
|
|
83
|
+
const files = readdirSync(wasmSourceDir);
|
|
84
|
+
const wasmJsFile = files.find((f) => f.startsWith('avatar_core_wasm') && f.endsWith('.js'));
|
|
85
|
+
if (wasmJsFile) {
|
|
86
|
+
const wasmJsSource = join(wasmSourceDir, wasmJsFile);
|
|
87
|
+
copyFileSync(wasmJsSource, wasmJsDest);
|
|
88
|
+
console.log(`✅ [avatarkit] Copied WASM JS file to dist/assets/avatar_core_wasm.js (from ${wasmJsFile})`);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
console.log('ℹ️ [avatarkit] WASM JS file not found (may be handled by Vite):', wasmSourceDir);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// 生成 _headers 文件(用于 Cloudflare Pages 等平台)
|
|
95
|
+
const headersContent = '/*.wasm\n Content-Type: application/wasm\n';
|
|
96
|
+
writeFileSync(headersDest, headersContent);
|
|
97
|
+
console.log('✅ [avatarkit] Created _headers file for Cloudflare Pages');
|
|
98
|
+
},
|
|
99
|
+
// 自动配置 Vite 选项
|
|
100
|
+
config() {
|
|
101
|
+
return {
|
|
102
|
+
optimizeDeps: {
|
|
103
|
+
exclude: ['@spatialwalk/avatarkit']
|
|
104
|
+
},
|
|
105
|
+
assetsInclude: ['**/*.wasm'],
|
|
106
|
+
build: {
|
|
107
|
+
assetsInlineLimit: 0, // 确保 WASM 文件不被内联
|
|
108
|
+
rollupOptions: {
|
|
109
|
+
output: {
|
|
110
|
+
assetFileNames: (assetInfo) => {
|
|
111
|
+
// WASM 文件使用固定名称
|
|
112
|
+
if (assetInfo.name?.endsWith('.wasm')) {
|
|
113
|
+
return 'assets/[name][extname]';
|
|
114
|
+
}
|
|
115
|
+
// 其他资源使用 hash
|
|
116
|
+
return 'assets/[name]-[hash][extname]';
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// 默认导出
|
|
126
|
+
export default avatarkitVitePlugin;
|