@vctrl/hooks 0.16.2 → 0.17.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.
- package/CHANGELOG.md +14 -0
- package/GLTFLoader-CygFefRr.cjs +1 -0
- package/GLTFLoader-NiJ7qzh1.js +1381 -0
- package/README.md +0 -20
- package/__vite-browser-external-Bn_csQuM.js +7 -0
- package/__vite-browser-external-CVmj8TrW.cjs +1 -0
- package/chunk-foFoljWS.js +23 -0
- package/chunk-gGpyby2o.cjs +1 -0
- package/index.cjs.js +1 -0
- package/index.d.ts +5 -0
- package/index.es.js +4 -0
- package/lib-DKn2MJ9X.js +4111 -0
- package/lib-DZ-MihdJ.cjs +2 -0
- package/model-exporter-SMSAY90N.cjs +1 -0
- package/model-exporter-cvguDGnn.js +6512 -0
- package/package.json +1 -1
- package/server-communication-Bj6ZxFw2.js +4659 -0
- package/server-communication-yXZR8eso.cjs +18 -0
- package/use-export-model/use-export-model.d.ts +14 -0
- package/use-export-model-CaijtW1c.cjs +1 -0
- package/use-export-model-DzVKdqnY.js +49 -0
- package/use-export-model.cjs.js +1 -0
- package/use-export-model.es.js +2 -0
- package/use-load-model/event-system.d.ts +7 -0
- package/use-load-model/index.d.ts +4 -0
- package/use-load-model/state.d.ts +19 -0
- package/use-load-model/types.d.ts +231 -0
- package/use-load-model/use-load-model.d.ts +30 -0
- package/use-load-model/utils/calculate-referenced-bytes.d.ts +8 -0
- package/use-load-model/utils/index.d.ts +4 -0
- package/use-load-model/utils/read-directory.d.ts +8 -0
- package/use-load-model/utils/reconstruct-files.d.ts +29 -0
- package/use-load-model/utils/resolve-scene-payload.d.ts +6 -0
- package/use-load-model-B6UXiFWh.js +517 -0
- package/use-load-model-CH-WPQ_s.cjs +1 -0
- package/use-load-model.cjs.js +1 -0
- package/use-load-model.es.js +18 -0
- package/{src/use-optimize-model/index.ts → use-optimize-model/index.d.ts} +2 -2
- package/use-optimize-model/state.d.ts +13 -0
- package/use-optimize-model/types.d.ts +39 -0
- package/use-optimize-model/use-calc-optimization-info.d.ts +6 -0
- package/use-optimize-model/use-optimize-model.d.ts +119 -0
- package/use-optimize-model/utils/index.d.ts +2 -0
- package/use-optimize-model/utils/texture-optimization.d.ts +17 -0
- package/use-optimize-model/utils/validation.d.ts +8 -0
- package/use-optimize-model-C-fzzKh6.cjs +1 -0
- package/use-optimize-model-Cb3WMvDZ.js +299 -0
- package/use-optimize-model.cjs.js +1 -0
- package/use-optimize-model.es.js +2 -0
- package/utils/server-communication.d.ts +122 -0
- package/project.json +0 -55
- package/src/index.ts +0 -38
- package/src/use-export-model/use-export-model.ts +0 -113
- package/src/use-load-model/event-system.ts +0 -46
- package/src/use-load-model/index.ts +0 -15
- package/src/use-load-model/model-context.tsx +0 -174
- package/src/use-load-model/state.ts +0 -75
- package/src/use-load-model/types.ts +0 -284
- package/src/use-load-model/use-load-model.ts +0 -682
- package/src/use-load-model/utils/calculate-referenced-bytes.ts +0 -123
- package/src/use-load-model/utils/index.ts +0 -7
- package/src/use-load-model/utils/read-directory.ts +0 -35
- package/src/use-load-model/utils/reconstruct-files.ts +0 -85
- package/src/use-load-model/utils/resolve-scene-payload.ts +0 -151
- package/src/use-optimize-model/state.ts +0 -40
- package/src/use-optimize-model/types.ts +0 -37
- package/src/use-optimize-model/use-calc-optimization-info.ts +0 -101
- package/src/use-optimize-model/use-optimize-model.ts +0 -467
- package/src/use-optimize-model/utils/index.ts +0 -29
- package/src/use-optimize-model/utils/texture-optimization.ts +0 -292
- package/src/use-optimize-model/utils/validation.ts +0 -62
- package/src/utils/server-communication.ts +0 -351
- package/tsconfig.json +0 -21
- package/tsconfig.lib.json +0 -16
- package/vite.config.ts +0 -77
- /package/{src/use-export-model/index.ts → use-export-model/index.d.ts} +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { DedupOptions, NormalsOptions, QuantizeOptions, SimplifyOptions, TextureCompressOptions } from '../../../core/src/model-optimizer/index.ts';
|
|
2
|
+
import { Object3D } from 'three';
|
|
3
|
+
import { ServerSceneData } from '../../../core/src/index.ts';
|
|
4
|
+
/**
|
|
5
|
+
* Custom React hook for optimizing 3D models using the ModelOptimizer from @vctrl/core.
|
|
6
|
+
*
|
|
7
|
+
* This hook provides a React-friendly interface to advanced model optimization capabilities,
|
|
8
|
+
* including mesh simplification, deduplication, quantization, normal optimization, and texture compression.
|
|
9
|
+
* It manages the optimization state and provides callbacks for each optimization operation.
|
|
10
|
+
*
|
|
11
|
+
* **Features:**
|
|
12
|
+
* - Mesh simplification using MeshoptSimplifier
|
|
13
|
+
* - Geometry deduplication to remove redundant data
|
|
14
|
+
* - Vertex attribute quantization to reduce file size
|
|
15
|
+
* - Normal vector optimization
|
|
16
|
+
* - Texture compression (server-side only)
|
|
17
|
+
* - Progress tracking and error handling
|
|
18
|
+
* - Optimization reports with before/after metrics
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const optimizer = useOptimizeModel()
|
|
22
|
+
*
|
|
23
|
+
* // Load a model
|
|
24
|
+
* await optimizer.load(threeJsScene)
|
|
25
|
+
*
|
|
26
|
+
* // Apply optimizations
|
|
27
|
+
* await optimizer.simplifyOptimization({ ratio: 0.5 })
|
|
28
|
+
* await optimizer.quantizeOptimization({ bits: 12 })
|
|
29
|
+
*
|
|
30
|
+
* // Get optimized model
|
|
31
|
+
* const optimizedBinary = await optimizer.getModel()
|
|
32
|
+
*
|
|
33
|
+
* @returns Object containing optimization methods, state, and report data
|
|
34
|
+
*/
|
|
35
|
+
declare const useOptimizeModel: () => {
|
|
36
|
+
/**
|
|
37
|
+
* Loads a Three.js Object3D model into the optimizer.
|
|
38
|
+
* Converts the scene to glTF format and generates an initial optimization report.
|
|
39
|
+
*
|
|
40
|
+
* @param model - The Three.js Object3D model to load
|
|
41
|
+
* @returns Promise that resolves when the model is loaded
|
|
42
|
+
*/
|
|
43
|
+
load: (model: Object3D) => Promise<void>;
|
|
44
|
+
loadFromServerSceneData: (sceneData: ServerSceneData) => Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Retrieves the current model as a binary Uint8Array in glTF (.glb) format.
|
|
47
|
+
*
|
|
48
|
+
* @returns Promise that resolves with the model binary or null if no model is loaded
|
|
49
|
+
*/
|
|
50
|
+
getModel: () => Promise<Uint8Array | null>;
|
|
51
|
+
isReady: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Exposes the underlying ModelOptimizer document instance for advanced use cases.
|
|
54
|
+
*/
|
|
55
|
+
_getDocument: () => import('@gltf-transform/core').Document | null;
|
|
56
|
+
/**
|
|
57
|
+
* Resets the optimizer, clearing all model data, reports, and state.
|
|
58
|
+
*/
|
|
59
|
+
reset: () => void;
|
|
60
|
+
/**
|
|
61
|
+
* Error object if any optimization operation failed, otherwise null.
|
|
62
|
+
*/
|
|
63
|
+
error: Error | null;
|
|
64
|
+
/**
|
|
65
|
+
* Boolean indicating if an optimization operation is currently in progress.
|
|
66
|
+
*/
|
|
67
|
+
loading: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Detailed optimization report containing metrics about the model
|
|
70
|
+
* (vertex count, triangle count, file size, etc.) before and after optimizations.
|
|
71
|
+
*/
|
|
72
|
+
report: import('../../../core/src/index.ts').OptimizationReport | null;
|
|
73
|
+
/**
|
|
74
|
+
* Calculated optimization information including file size reduction percentages
|
|
75
|
+
* and comparative metrics.
|
|
76
|
+
*/
|
|
77
|
+
info: import('./types').OptimizationInfo;
|
|
78
|
+
/**
|
|
79
|
+
* Simplifies the model by reducing polygon count using MeshoptSimplifier.
|
|
80
|
+
* Maintains visual quality while improving performance.
|
|
81
|
+
*
|
|
82
|
+
* @param options - Simplification options (ratio, error threshold)
|
|
83
|
+
* @returns Promise that resolves when simplification is complete
|
|
84
|
+
*/
|
|
85
|
+
simplifyOptimization: (options?: SimplifyOptions) => Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Removes duplicate vertices, primitives, and other redundant data.
|
|
88
|
+
* Reduces file size without affecting visual appearance.
|
|
89
|
+
*
|
|
90
|
+
* @param options - Deduplication options
|
|
91
|
+
* @returns Promise that resolves when deduplication is complete
|
|
92
|
+
*/
|
|
93
|
+
dedupOptimization: (options?: DedupOptions) => Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Quantizes vertex attributes to use fewer bits per value.
|
|
96
|
+
* Reduces file size with minimal visual quality loss.
|
|
97
|
+
*
|
|
98
|
+
* @param options - Quantization options (bit depth)
|
|
99
|
+
* @returns Promise that resolves when quantization is complete
|
|
100
|
+
*/
|
|
101
|
+
quantizeOptimization: (options?: QuantizeOptions) => Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Optimizes normal vectors by removing, generating, or cleaning up normal data.
|
|
104
|
+
*
|
|
105
|
+
* @param options - Normal optimization options
|
|
106
|
+
* @returns Promise that resolves when optimization is complete
|
|
107
|
+
*/
|
|
108
|
+
normalsOptimization: (options?: NormalsOptions) => Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Compresses textures using advanced formats (requires server-side processing).
|
|
111
|
+
* Note: Only works in Node.js environments with Sharp library available.
|
|
112
|
+
*
|
|
113
|
+
* @param options - Texture compression options (format, quality)
|
|
114
|
+
* @returns Promise that resolves when compression is complete
|
|
115
|
+
* @throws Error if Sharp is not available
|
|
116
|
+
*/
|
|
117
|
+
texturesOptimization: (options?: TextureCompressOptions) => Promise<void>;
|
|
118
|
+
};
|
|
119
|
+
export default useOptimizeModel;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ModelOptimizer, TextureCompressOptions } from '../../../../core/src/model-optimizer/index.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Performs server-side texture optimization.
|
|
4
|
+
*/
|
|
5
|
+
export declare const performServerSideTextureOptimization: (optimizer: ModelOptimizer, options: TextureCompressOptions) => Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Handles client-side texture optimization (throws error as not supported in browser).
|
|
8
|
+
*/
|
|
9
|
+
export declare const performClientSideTextureOptimization: () => never;
|
|
10
|
+
/**
|
|
11
|
+
* Determines if server-side optimization should be used.
|
|
12
|
+
*/
|
|
13
|
+
export declare const shouldUseServerOptimization: (options?: TextureCompressOptions) => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Main texture optimization function that orchestrates the process.
|
|
16
|
+
*/
|
|
17
|
+
export declare const optimizeTextures: (optimizer: ModelOptimizer, options?: TextureCompressOptions) => Promise<void>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates that the response contains the expected content type.
|
|
3
|
+
*/
|
|
4
|
+
export declare const validateResponseContentType: (response: Response) => void;
|
|
5
|
+
/**
|
|
6
|
+
* Validates a server response and throws appropriate errors.
|
|
7
|
+
*/
|
|
8
|
+
export declare const validateServerResponse: (response: Response) => Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require(`./chunk-gGpyby2o.cjs`);const e=require(`./server-communication-yXZR8eso.cjs`);let t=require(`react`);var n={info:null,error:null,loading:!1,report:null},r=(e,t)=>{switch(t.type){case`LOAD_START`:return{...e,loading:!0,error:null};case`LOAD_SUCCESS`:return{...e,loading:!1,report:t.payload.report};case`LOAD_ERROR`:return{...e,loading:!1,error:t.payload};case`RESET`:return{...n};default:return e}};function i(e){return e?{verticesCount:e.stats.vertices.after??0,primitivesCount:e.stats.triangles.after??0,meshesCount:e.stats.meshes.after??0,textureAssetCount:e.stats.texturesCount.after??0,materialsCount:e.stats.materials.after??0,sceneBytes:e.optimizedSize??0}:{verticesCount:0,primitivesCount:0,meshesCount:0,textureAssetCount:0,materialsCount:0,sceneBytes:0}}var a=e=>{let n=(0,t.useRef)(null),[r,a]=(0,t.useState)(!1),o=e.report;return(0,t.useEffect)(()=>{!r&&o&&(n.current=o,a(!0))},[r,o]),{info:(0,t.useMemo)(()=>{let e=i(n.current),t=i(o);return{initial:{verticesCount:e.verticesCount,primitivesCount:e.primitivesCount,meshesCount:e.meshesCount,texturesCount:e.textureAssetCount,sceneBytes:e.sceneBytes},optimized:{verticesCount:t.verticesCount,primitivesCount:t.primitivesCount,meshesCount:t.meshesCount,texturesCount:t.textureAssetCount,sceneBytes:t.sceneBytes},improvement:{verticesCount:e.verticesCount-t.verticesCount,primitivesCount:e.primitivesCount-t.primitivesCount,meshesCount:e.meshesCount-t.meshesCount,texturesCount:e.textureAssetCount-t.textureAssetCount,sceneBytes:e.sceneBytes-t.sceneBytes}}},[o]),reset:(0,t.useCallback)(()=>{n.current=null,a(!1)},[])}},o=e=>{let t=e.headers.get(`content-type`);if(!t)throw Error(`Missing content type from server response`);if(t.includes(`application/json`))throw Error(`Unexpected JSON success response from texture optimizer`);if(!t.startsWith(`image/`)&&!t.includes(`application/octet-stream`))throw Error(`Unexpected content type from server: ${t}`)},s=async e=>{if(e.ok){if(e.headers.get(`content-type`)?.includes(`application/json`)){let t=await e.json();throw Error(t.error||t.details||`Server returned error`)}o(e)}},c=2e4,l=2,u=50*1024*1024,d=4,f=new Set([408,425,429,500,502,503,504]),p=async e=>{await new Promise(t=>setTimeout(t,e))},m=e=>{let t=e.headers.get(`content-type`);return t&&t.split(`;`)[0]?.trim()||`application/octet-stream`},h=e=>{let t=e.headers.get(`X-Texture-File-Name`)||e.headers.get(`X-Texture-Name`);if(!t||t.trim().length===0)throw Error(`Optimized texture response is missing a canonical file name`);return t.trim()},g=async(e,t,n,r,i)=>{let a=0;for(;;){let o=new AbortController,s=setTimeout(()=>o.abort(),r);try{let r=await fetch(e,{method:`POST`,headers:t,body:n,signal:o.signal});if(!r.ok&&f.has(r.status)&&a<i){a+=1,await p(200*a+Math.floor(Math.random()*120));continue}return r}catch(e){if(a>=i)throw e;a+=1,await p(200*a+Math.floor(Math.random()*120))}finally{clearTimeout(s)}}},_=async(t,n,r,i)=>{let a=t.getTexturePayload(n.index),o=r.maxTextureUploadBytes??u;if(a.image.byteLength>o)throw Error(`Texture ${n.index} payload (${a.image.byteLength} bytes) exceeds maxTextureUploadBytes (${o} bytes). Increase maxTextureUploadBytes to allow optimizing larger source textures.`);let d=new Uint8Array(a.image.byteLength);d.set(a.image);let f=d.buffer,{serverOptions:p,..._}=r,v=e.t.createRequestHeaders(i,{"Content-Type":`application/octet-stream`,"X-Texture-Index":String(n.index),"X-Texture-Name":a.fileName,"X-Texture-File-Name":a.fileName,"X-Texture-Mime-Type":a.mimeType,"X-Optimize-Options":JSON.stringify(_)}),y=await g(i.endpoint,v,f,r.requestTimeoutMs??c,r.maxRetries??l);y.ok||await e.t.handleServerResponseError(y),await s(y);let b=Number.parseInt(y.headers.get(`X-Texture-Index`)||``,10);if(!Number.isFinite(b)||b!==n.index)throw Error(`Texture identity mismatch. Expected ${n.index}, received ${y.headers.get(`X-Texture-Index`)}`);let x=new Uint8Array(await y.arrayBuffer());if(x.byteLength===0)throw Error(`Empty optimized payload for texture ${n.index}`);let S=h(y);t.replaceTexturePayload(n.index,x,m(y),S)},v=async(t,n)=>{let r=e.t.createDefaultServerOptions(n.serverOptions),i=t.listTextureDescriptors(),a=[],o=0,s=n.maxConcurrentRequests??d;try{for(let e=0;e<i.length;e+=s){let c=i.slice(e,e+s),l=await Promise.allSettled(c.map(async e=>(await _(t,e,n,r),e.index)));for(let[e,t]of l.entries()){if(t.status===`fulfilled`){o+=1;continue}a.push({index:c[e].index,reason:t.reason instanceof Error?t.reason.message:String(t.reason)})}}if(o>0&&t.addAppliedOptimization(`texture compression`),a.length>0){let e=a.map(e=>`#${e.index}: ${e.reason}`).join(`; `),t=o===0?`Texture optimization failed for all textures.`:`Texture optimization failed for ${a.length} of ${i.length} textures.`;throw Error(`${t} ${e}`)}}catch(e){throw console.error(`Server-side texture compression failed:`,e),Error(`Server-side texture compression failed: ${e}. Client-side texture compression is not supported in browser environments.`,{cause:e})}},y=()=>{throw console.warn(`Client-side texture compression is not supported in browser environments. Please enable server-side compression by setting serverOptions.enabled = true.`),Error(`Texture compression requires server-side processing. Enable serverOptions.enabled = true in your options.`)},b=e=>e?.serverOptions?.enabled??!1,x=async(e,t)=>{e.hasModel()&&(b(t)&&t?await v(e,t):y())},S=()=>{let[i,o]=(0,t.useReducer)(r,n),{report:s,error:c,loading:l}=i,{info:u,reset:d}=a(i),f=(0,t.useRef)(new e.c);(0,t.useEffect)(()=>{let e=f.current;return()=>{e.reset()}},[]);let p=(0,t.useCallback)(async e=>{o({type:`LOAD_START`});try{let t=f.current;await t.loadFromThreeJS(e),o({type:`LOAD_SUCCESS`,payload:{report:await t.getReport()}})}catch(e){o({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading model:`,e)}},[]),m=(0,t.useCallback)(async t=>{o({type:`LOAD_START`});try{let n=f.current,r={};for(let n of Object.values(t.assetData??{}))r[n.fileName]=e.o(n);let i={json:t.gltfJson,resources:r};await n.loadFromJSON(i),o({type:`LOAD_SUCCESS`,payload:{report:await n.getReport()}})}catch(e){throw o({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading server scene into optimizer:`,e),e}},[]),h=(0,t.useCallback)(async e=>{if(f.current.hasModel())try{await f.current.simplify(e),o({type:`LOAD_SUCCESS`,payload:{report:await f.current.getReport()}})}catch(e){console.error(`Simplification failed:`,e)}},[]),g=(0,t.useCallback)(async e=>{if(f.current.hasModel())try{await f.current.deduplicate(e),o({type:`LOAD_SUCCESS`,payload:{report:await f.current.getReport()}})}catch(e){console.error(`Deduplication failed:`,e)}},[]),_=(0,t.useCallback)(async e=>{if(f.current.hasModel())try{await f.current.quantize(e),o({type:`LOAD_SUCCESS`,payload:{report:await f.current.getReport()}})}catch(e){console.error(`Quantization failed:`,e)}},[]),v=(0,t.useCallback)(async e=>{if(f.current.hasModel())try{await f.current.optimizeNormals(e),o({type:`LOAD_SUCCESS`,payload:{report:await f.current.getReport()}})}catch(e){console.error(`Normals optimization failed:`,e)}},[]),y=(0,t.useCallback)(async e=>{try{await x(f.current,e),o({type:`LOAD_SUCCESS`,payload:{report:await f.current.getReport()}})}catch(e){throw console.error(`Texture optimization failed:`,e),e}},[]),b=(0,t.useCallback)(async()=>{if(!f.current.hasModel())return null;try{return await f.current.export()}catch(e){return console.error(`Error getting model binary:`,e),null}},[]),S=(0,t.useCallback)(()=>f.current.hasModel()?f.current.document:null,[]);return{load:p,loadFromServerSceneData:m,getModel:b,isReady:!!S()&&!l,_getDocument:S,reset:(0,t.useCallback)(()=>{f.current.reset(),o({type:`RESET`}),d()},[d]),error:c,loading:l,report:s,info:u,simplifyOptimization:h,dedupOptimization:g,quantizeOptimization:_,normalsOptimization:v,texturesOptimization:y}};Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return S}});
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { c as e, o as t, t as n } from "./server-communication-Bj6ZxFw2.js";
|
|
2
|
+
import { useCallback as r, useEffect as i, useMemo as a, useReducer as o, useRef as s, useState as c } from "react";
|
|
3
|
+
//#region packages/hooks/src/use-optimize-model/state.ts
|
|
4
|
+
var l = {
|
|
5
|
+
info: null,
|
|
6
|
+
error: null,
|
|
7
|
+
loading: !1,
|
|
8
|
+
report: null
|
|
9
|
+
}, u = (e, t) => {
|
|
10
|
+
switch (t.type) {
|
|
11
|
+
case "LOAD_START": return {
|
|
12
|
+
...e,
|
|
13
|
+
loading: !0,
|
|
14
|
+
error: null
|
|
15
|
+
};
|
|
16
|
+
case "LOAD_SUCCESS": return {
|
|
17
|
+
...e,
|
|
18
|
+
loading: !1,
|
|
19
|
+
report: t.payload.report
|
|
20
|
+
};
|
|
21
|
+
case "LOAD_ERROR": return {
|
|
22
|
+
...e,
|
|
23
|
+
loading: !1,
|
|
24
|
+
error: t.payload
|
|
25
|
+
};
|
|
26
|
+
case "RESET": return { ...l };
|
|
27
|
+
default: return e;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region packages/hooks/src/use-optimize-model/use-calc-optimization-info.ts
|
|
32
|
+
function d(e) {
|
|
33
|
+
return e ? {
|
|
34
|
+
verticesCount: e.stats.vertices.after ?? 0,
|
|
35
|
+
primitivesCount: e.stats.triangles.after ?? 0,
|
|
36
|
+
meshesCount: e.stats.meshes.after ?? 0,
|
|
37
|
+
textureAssetCount: e.stats.texturesCount.after ?? 0,
|
|
38
|
+
materialsCount: e.stats.materials.after ?? 0,
|
|
39
|
+
sceneBytes: e.optimizedSize ?? 0
|
|
40
|
+
} : {
|
|
41
|
+
verticesCount: 0,
|
|
42
|
+
primitivesCount: 0,
|
|
43
|
+
meshesCount: 0,
|
|
44
|
+
textureAssetCount: 0,
|
|
45
|
+
materialsCount: 0,
|
|
46
|
+
sceneBytes: 0
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
var f = (e) => {
|
|
50
|
+
let t = s(null), [n, o] = c(!1), l = e.report;
|
|
51
|
+
return i(() => {
|
|
52
|
+
!n && l && (t.current = l, o(!0));
|
|
53
|
+
}, [n, l]), {
|
|
54
|
+
info: a(() => {
|
|
55
|
+
let e = d(t.current), n = d(l);
|
|
56
|
+
return {
|
|
57
|
+
initial: {
|
|
58
|
+
verticesCount: e.verticesCount,
|
|
59
|
+
primitivesCount: e.primitivesCount,
|
|
60
|
+
meshesCount: e.meshesCount,
|
|
61
|
+
texturesCount: e.textureAssetCount,
|
|
62
|
+
sceneBytes: e.sceneBytes
|
|
63
|
+
},
|
|
64
|
+
optimized: {
|
|
65
|
+
verticesCount: n.verticesCount,
|
|
66
|
+
primitivesCount: n.primitivesCount,
|
|
67
|
+
meshesCount: n.meshesCount,
|
|
68
|
+
texturesCount: n.textureAssetCount,
|
|
69
|
+
sceneBytes: n.sceneBytes
|
|
70
|
+
},
|
|
71
|
+
improvement: {
|
|
72
|
+
verticesCount: e.verticesCount - n.verticesCount,
|
|
73
|
+
primitivesCount: e.primitivesCount - n.primitivesCount,
|
|
74
|
+
meshesCount: e.meshesCount - n.meshesCount,
|
|
75
|
+
texturesCount: e.textureAssetCount - n.textureAssetCount,
|
|
76
|
+
sceneBytes: e.sceneBytes - n.sceneBytes
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}, [l]),
|
|
80
|
+
reset: r(() => {
|
|
81
|
+
t.current = null, o(!1);
|
|
82
|
+
}, [])
|
|
83
|
+
};
|
|
84
|
+
}, p = (e) => {
|
|
85
|
+
let t = e.headers.get("content-type");
|
|
86
|
+
if (!t) throw Error("Missing content type from server response");
|
|
87
|
+
if (t.includes("application/json")) throw Error("Unexpected JSON success response from texture optimizer");
|
|
88
|
+
if (!t.startsWith("image/") && !t.includes("application/octet-stream")) throw Error(`Unexpected content type from server: ${t}`);
|
|
89
|
+
}, m = async (e) => {
|
|
90
|
+
if (e.ok) {
|
|
91
|
+
if (e.headers.get("content-type")?.includes("application/json")) {
|
|
92
|
+
let t = await e.json();
|
|
93
|
+
throw Error(t.error || t.details || "Server returned error");
|
|
94
|
+
}
|
|
95
|
+
p(e);
|
|
96
|
+
}
|
|
97
|
+
}, h = 2e4, g = 2, _ = 50 * 1024 * 1024, v = 4, y = new Set([
|
|
98
|
+
408,
|
|
99
|
+
425,
|
|
100
|
+
429,
|
|
101
|
+
500,
|
|
102
|
+
502,
|
|
103
|
+
503,
|
|
104
|
+
504
|
|
105
|
+
]), b = async (e) => {
|
|
106
|
+
await new Promise((t) => setTimeout(t, e));
|
|
107
|
+
}, x = (e) => {
|
|
108
|
+
let t = e.headers.get("content-type");
|
|
109
|
+
return t && t.split(";")[0]?.trim() || "application/octet-stream";
|
|
110
|
+
}, S = (e) => {
|
|
111
|
+
let t = e.headers.get("X-Texture-File-Name") || e.headers.get("X-Texture-Name");
|
|
112
|
+
if (!t || t.trim().length === 0) throw Error("Optimized texture response is missing a canonical file name");
|
|
113
|
+
return t.trim();
|
|
114
|
+
}, C = async (e, t, n, r, i) => {
|
|
115
|
+
let a = 0;
|
|
116
|
+
for (;;) {
|
|
117
|
+
let o = new AbortController(), s = setTimeout(() => o.abort(), r);
|
|
118
|
+
try {
|
|
119
|
+
let r = await fetch(e, {
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: t,
|
|
122
|
+
body: n,
|
|
123
|
+
signal: o.signal
|
|
124
|
+
});
|
|
125
|
+
if (!r.ok && y.has(r.status) && a < i) {
|
|
126
|
+
a += 1, await b(200 * a + Math.floor(Math.random() * 120));
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
return r;
|
|
130
|
+
} catch (e) {
|
|
131
|
+
if (a >= i) throw e;
|
|
132
|
+
a += 1, await b(200 * a + Math.floor(Math.random() * 120));
|
|
133
|
+
} finally {
|
|
134
|
+
clearTimeout(s);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}, w = async (e, t, r, i) => {
|
|
138
|
+
let a = e.getTexturePayload(t.index), o = r.maxTextureUploadBytes ?? _;
|
|
139
|
+
if (a.image.byteLength > o) throw Error(`Texture ${t.index} payload (${a.image.byteLength} bytes) exceeds maxTextureUploadBytes (${o} bytes). Increase maxTextureUploadBytes to allow optimizing larger source textures.`);
|
|
140
|
+
let s = new Uint8Array(a.image.byteLength);
|
|
141
|
+
s.set(a.image);
|
|
142
|
+
let c = s.buffer, { serverOptions: l, ...u } = r, d = n.createRequestHeaders(i, {
|
|
143
|
+
"Content-Type": "application/octet-stream",
|
|
144
|
+
"X-Texture-Index": String(t.index),
|
|
145
|
+
"X-Texture-Name": a.fileName,
|
|
146
|
+
"X-Texture-File-Name": a.fileName,
|
|
147
|
+
"X-Texture-Mime-Type": a.mimeType,
|
|
148
|
+
"X-Optimize-Options": JSON.stringify(u)
|
|
149
|
+
}), f = await C(i.endpoint, d, c, r.requestTimeoutMs ?? h, r.maxRetries ?? g);
|
|
150
|
+
f.ok || await n.handleServerResponseError(f), await m(f);
|
|
151
|
+
let p = Number.parseInt(f.headers.get("X-Texture-Index") || "", 10);
|
|
152
|
+
if (!Number.isFinite(p) || p !== t.index) throw Error(`Texture identity mismatch. Expected ${t.index}, received ${f.headers.get("X-Texture-Index")}`);
|
|
153
|
+
let v = new Uint8Array(await f.arrayBuffer());
|
|
154
|
+
if (v.byteLength === 0) throw Error(`Empty optimized payload for texture ${t.index}`);
|
|
155
|
+
let y = S(f);
|
|
156
|
+
e.replaceTexturePayload(t.index, v, x(f), y);
|
|
157
|
+
}, T = async (e, t) => {
|
|
158
|
+
let r = n.createDefaultServerOptions(t.serverOptions), i = e.listTextureDescriptors(), a = [], o = 0, s = t.maxConcurrentRequests ?? v;
|
|
159
|
+
try {
|
|
160
|
+
for (let n = 0; n < i.length; n += s) {
|
|
161
|
+
let c = i.slice(n, n + s), l = await Promise.allSettled(c.map(async (n) => (await w(e, n, t, r), n.index)));
|
|
162
|
+
for (let [e, t] of l.entries()) {
|
|
163
|
+
if (t.status === "fulfilled") {
|
|
164
|
+
o += 1;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
a.push({
|
|
168
|
+
index: c[e].index,
|
|
169
|
+
reason: t.reason instanceof Error ? t.reason.message : String(t.reason)
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (o > 0 && e.addAppliedOptimization("texture compression"), a.length > 0) {
|
|
174
|
+
let e = a.map((e) => `#${e.index}: ${e.reason}`).join("; "), t = o === 0 ? "Texture optimization failed for all textures." : `Texture optimization failed for ${a.length} of ${i.length} textures.`;
|
|
175
|
+
throw Error(`${t} ${e}`);
|
|
176
|
+
}
|
|
177
|
+
} catch (e) {
|
|
178
|
+
throw console.error("Server-side texture compression failed:", e), Error(`Server-side texture compression failed: ${e}. Client-side texture compression is not supported in browser environments.`, { cause: e });
|
|
179
|
+
}
|
|
180
|
+
}, E = () => {
|
|
181
|
+
throw console.warn("Client-side texture compression is not supported in browser environments. Please enable server-side compression by setting serverOptions.enabled = true."), Error("Texture compression requires server-side processing. Enable serverOptions.enabled = true in your options.");
|
|
182
|
+
}, D = (e) => e?.serverOptions?.enabled ?? !1, O = async (e, t) => {
|
|
183
|
+
e.hasModel() && (D(t) && t ? await T(e, t) : E());
|
|
184
|
+
}, k = () => {
|
|
185
|
+
let [n, a] = o(u, l), { report: c, error: d, loading: p } = n, { info: m, reset: h } = f(n), g = s(new e());
|
|
186
|
+
i(() => {
|
|
187
|
+
let e = g.current;
|
|
188
|
+
return () => {
|
|
189
|
+
e.reset();
|
|
190
|
+
};
|
|
191
|
+
}, []);
|
|
192
|
+
let _ = r(async (e) => {
|
|
193
|
+
a({ type: "LOAD_START" });
|
|
194
|
+
try {
|
|
195
|
+
let t = g.current;
|
|
196
|
+
await t.loadFromThreeJS(e), a({
|
|
197
|
+
type: "LOAD_SUCCESS",
|
|
198
|
+
payload: { report: await t.getReport() }
|
|
199
|
+
});
|
|
200
|
+
} catch (e) {
|
|
201
|
+
a({
|
|
202
|
+
type: "LOAD_ERROR",
|
|
203
|
+
payload: e
|
|
204
|
+
}), console.error("Error loading model:", e);
|
|
205
|
+
}
|
|
206
|
+
}, []), v = r(async (e) => {
|
|
207
|
+
a({ type: "LOAD_START" });
|
|
208
|
+
try {
|
|
209
|
+
let n = g.current, r = {};
|
|
210
|
+
for (let n of Object.values(e.assetData ?? {})) r[n.fileName] = t(n);
|
|
211
|
+
let i = {
|
|
212
|
+
json: e.gltfJson,
|
|
213
|
+
resources: r
|
|
214
|
+
};
|
|
215
|
+
await n.loadFromJSON(i), a({
|
|
216
|
+
type: "LOAD_SUCCESS",
|
|
217
|
+
payload: { report: await n.getReport() }
|
|
218
|
+
});
|
|
219
|
+
} catch (e) {
|
|
220
|
+
throw a({
|
|
221
|
+
type: "LOAD_ERROR",
|
|
222
|
+
payload: e
|
|
223
|
+
}), console.error("Error loading server scene into optimizer:", e), e;
|
|
224
|
+
}
|
|
225
|
+
}, []), y = r(async (e) => {
|
|
226
|
+
if (g.current.hasModel()) try {
|
|
227
|
+
await g.current.simplify(e), a({
|
|
228
|
+
type: "LOAD_SUCCESS",
|
|
229
|
+
payload: { report: await g.current.getReport() }
|
|
230
|
+
});
|
|
231
|
+
} catch (e) {
|
|
232
|
+
console.error("Simplification failed:", e);
|
|
233
|
+
}
|
|
234
|
+
}, []), b = r(async (e) => {
|
|
235
|
+
if (g.current.hasModel()) try {
|
|
236
|
+
await g.current.deduplicate(e), a({
|
|
237
|
+
type: "LOAD_SUCCESS",
|
|
238
|
+
payload: { report: await g.current.getReport() }
|
|
239
|
+
});
|
|
240
|
+
} catch (e) {
|
|
241
|
+
console.error("Deduplication failed:", e);
|
|
242
|
+
}
|
|
243
|
+
}, []), x = r(async (e) => {
|
|
244
|
+
if (g.current.hasModel()) try {
|
|
245
|
+
await g.current.quantize(e), a({
|
|
246
|
+
type: "LOAD_SUCCESS",
|
|
247
|
+
payload: { report: await g.current.getReport() }
|
|
248
|
+
});
|
|
249
|
+
} catch (e) {
|
|
250
|
+
console.error("Quantization failed:", e);
|
|
251
|
+
}
|
|
252
|
+
}, []), S = r(async (e) => {
|
|
253
|
+
if (g.current.hasModel()) try {
|
|
254
|
+
await g.current.optimizeNormals(e), a({
|
|
255
|
+
type: "LOAD_SUCCESS",
|
|
256
|
+
payload: { report: await g.current.getReport() }
|
|
257
|
+
});
|
|
258
|
+
} catch (e) {
|
|
259
|
+
console.error("Normals optimization failed:", e);
|
|
260
|
+
}
|
|
261
|
+
}, []), C = r(async (e) => {
|
|
262
|
+
try {
|
|
263
|
+
await O(g.current, e), a({
|
|
264
|
+
type: "LOAD_SUCCESS",
|
|
265
|
+
payload: { report: await g.current.getReport() }
|
|
266
|
+
});
|
|
267
|
+
} catch (e) {
|
|
268
|
+
throw console.error("Texture optimization failed:", e), e;
|
|
269
|
+
}
|
|
270
|
+
}, []), w = r(async () => {
|
|
271
|
+
if (!g.current.hasModel()) return null;
|
|
272
|
+
try {
|
|
273
|
+
return await g.current.export();
|
|
274
|
+
} catch (e) {
|
|
275
|
+
return console.error("Error getting model binary:", e), null;
|
|
276
|
+
}
|
|
277
|
+
}, []), T = r(() => g.current.hasModel() ? g.current.document : null, []);
|
|
278
|
+
return {
|
|
279
|
+
load: _,
|
|
280
|
+
loadFromServerSceneData: v,
|
|
281
|
+
getModel: w,
|
|
282
|
+
isReady: !!T() && !p,
|
|
283
|
+
_getDocument: T,
|
|
284
|
+
reset: r(() => {
|
|
285
|
+
g.current.reset(), a({ type: "RESET" }), h();
|
|
286
|
+
}, [h]),
|
|
287
|
+
error: d,
|
|
288
|
+
loading: p,
|
|
289
|
+
report: c,
|
|
290
|
+
info: m,
|
|
291
|
+
simplifyOptimization: y,
|
|
292
|
+
dedupOptimization: b,
|
|
293
|
+
quantizeOptimization: x,
|
|
294
|
+
normalsOptimization: S,
|
|
295
|
+
texturesOptimization: C
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
//#endregion
|
|
299
|
+
export { k as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./chunk-gGpyby2o.cjs`);const e=require(`./use-optimize-model-C-fzzKh6.cjs`);exports.useOptimizeModel=e.t;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { ServerOptions, TextureBinaryPayload, TextureCompressOptions } from '../../../core/src/index.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for a server request.
|
|
4
|
+
*/
|
|
5
|
+
export interface ServerRequestConfig {
|
|
6
|
+
/** Server endpoint URL or path */
|
|
7
|
+
endpoint: string;
|
|
8
|
+
/** HTTP method (GET, POST, etc.) */
|
|
9
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
10
|
+
/** Request body (FormData, JSON, or other) */
|
|
11
|
+
body?: FormData | Record<string, unknown> | string;
|
|
12
|
+
/** Optional server options (headers, auth, etc.) */
|
|
13
|
+
serverOptions?: ServerOptions;
|
|
14
|
+
/** Content type for JSON bodies (default: 'application/json') */
|
|
15
|
+
contentType?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Unified server communication service for handling HTTP requests.
|
|
19
|
+
* Provides a consistent interface for API calls with built-in error handling,
|
|
20
|
+
* authentication, and response parsing.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* // Simple GET request
|
|
25
|
+
* const data = await ServerCommunicationService.request<SceneData>({
|
|
26
|
+
* endpoint: '/api/load-scene',
|
|
27
|
+
* method: 'GET'
|
|
28
|
+
* })
|
|
29
|
+
*
|
|
30
|
+
* // POST with FormData
|
|
31
|
+
* const formData = new FormData()
|
|
32
|
+
* formData.append('model', file)
|
|
33
|
+
* const result = await ServerCommunicationService.request({
|
|
34
|
+
* endpoint: '/api/optimize',
|
|
35
|
+
* method: 'POST',
|
|
36
|
+
* body: formData,
|
|
37
|
+
* serverOptions: { apiKey: 'secret' }
|
|
38
|
+
* })
|
|
39
|
+
*
|
|
40
|
+
* // POST with JSON
|
|
41
|
+
* const response = await ServerCommunicationService.request({
|
|
42
|
+
* endpoint: '/api/save',
|
|
43
|
+
* method: 'POST',
|
|
44
|
+
* body: { sceneId: '123', settings: {...} },
|
|
45
|
+
* serverOptions: { headers: { 'X-Custom': 'value' } }
|
|
46
|
+
* })
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare class ServerCommunicationService {
|
|
50
|
+
private static extractApiData;
|
|
51
|
+
/**
|
|
52
|
+
* Creates default server options with required endpoint.
|
|
53
|
+
* Merges provided options with defaults.
|
|
54
|
+
*/
|
|
55
|
+
static createDefaultServerOptions(serverOptions?: ServerOptions): ServerOptions & Required<Pick<ServerOptions, 'endpoint'>>;
|
|
56
|
+
/**
|
|
57
|
+
* Creates request headers for server communication.
|
|
58
|
+
* Includes authentication token if apiKey is provided.
|
|
59
|
+
*/
|
|
60
|
+
static createRequestHeaders(serverOptions?: ServerOptions, additionalHeaders?: HeadersInit): HeadersInit;
|
|
61
|
+
/**
|
|
62
|
+
* Extracts error message from a failed response.
|
|
63
|
+
* Attempts to parse JSON error data, falls back to text.
|
|
64
|
+
*/
|
|
65
|
+
static extractErrorMessage(response: Response): Promise<string>;
|
|
66
|
+
/**
|
|
67
|
+
* Handles server response errors and throws appropriate errors.
|
|
68
|
+
*/
|
|
69
|
+
static handleServerResponseError(response: Response): Promise<never>;
|
|
70
|
+
/**
|
|
71
|
+
* Prepares FormData for a single texture optimization request.
|
|
72
|
+
*/
|
|
73
|
+
static prepareTextureOptimizationFormData(texture: TextureBinaryPayload, options?: TextureCompressOptions): FormData;
|
|
74
|
+
/**
|
|
75
|
+
* Performs a generic HTTP request with error handling.
|
|
76
|
+
* Returns parsed response data.
|
|
77
|
+
*
|
|
78
|
+
* @template T - Expected response data type
|
|
79
|
+
* @param config - Request configuration
|
|
80
|
+
* @returns Promise resolving to parsed response data
|
|
81
|
+
* @throws Error if request fails or response is not ok
|
|
82
|
+
*/
|
|
83
|
+
static request<T = unknown>(config: ServerRequestConfig): Promise<T>;
|
|
84
|
+
/**
|
|
85
|
+
* Performs a GET request.
|
|
86
|
+
* Convenience method for common GET operations.
|
|
87
|
+
*/
|
|
88
|
+
static get<T = unknown>(endpoint: string, serverOptions?: ServerOptions): Promise<T>;
|
|
89
|
+
/**
|
|
90
|
+
* Performs a POST request with JSON body.
|
|
91
|
+
* Convenience method for common POST operations.
|
|
92
|
+
*/
|
|
93
|
+
static post<T = unknown>(endpoint: string, body: Record<string, unknown>, serverOptions?: ServerOptions): Promise<T>;
|
|
94
|
+
/**
|
|
95
|
+
* Performs a POST request with FormData body.
|
|
96
|
+
* Convenience method for file uploads.
|
|
97
|
+
*/
|
|
98
|
+
static postFormData<T = unknown>(endpoint: string, formData: FormData, serverOptions?: ServerOptions): Promise<T>;
|
|
99
|
+
/**
|
|
100
|
+
* Fetches scene data from the server.
|
|
101
|
+
* Specialized method for loading 3D scenes with their associated settings.
|
|
102
|
+
*
|
|
103
|
+
* This method handles the vectreal-platform API format which uses:
|
|
104
|
+
* - FormData with 'action' and 'sceneId' parameters
|
|
105
|
+
* - POST request to the scene-settings endpoint
|
|
106
|
+
* - Returns scene data including GLTF JSON and asset data
|
|
107
|
+
*
|
|
108
|
+
* @param sceneId - The unique identifier of the scene to load
|
|
109
|
+
* @param serverOptions - Optional server configuration (endpoint, auth, headers)
|
|
110
|
+
* @returns Promise resolving to the scene data
|
|
111
|
+
* @throws Error if the request fails or scene doesn't exist
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* const sceneData = await ServerCommunicationService.loadScene('abc-123', {
|
|
116
|
+
* endpoint: '/api/scenes/abc-123',
|
|
117
|
+
* apiKey: 'optional-auth-token'
|
|
118
|
+
* })
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
static loadScene<T = unknown>(sceneId: string, serverOptions?: ServerOptions): Promise<T>;
|
|
122
|
+
}
|
package/project.json
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "vctrl/hooks",
|
|
3
|
-
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
-
"sourceRoot": "packages/hooks/src",
|
|
5
|
-
"projectType": "library",
|
|
6
|
-
"tags": [],
|
|
7
|
-
"targets": {
|
|
8
|
-
"lint": {
|
|
9
|
-
"executor": "@nx/eslint:lint",
|
|
10
|
-
"outputs": ["{options.outputFile}"],
|
|
11
|
-
"options": {
|
|
12
|
-
"fix": true,
|
|
13
|
-
"lintFilePatterns": [
|
|
14
|
-
"packages/hooks/**/*.{ts,tsx,js,jsx}",
|
|
15
|
-
"packages/hooks/package.json"
|
|
16
|
-
]
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"publish": {
|
|
20
|
-
"dependsOn": ["copy-md"],
|
|
21
|
-
"executor": "nx:run-commands",
|
|
22
|
-
"outputs": [],
|
|
23
|
-
"options": {
|
|
24
|
-
"command": "npm publish --access public",
|
|
25
|
-
"cwd": "build/packages/vctrl/hooks"
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
"copy-md": {
|
|
29
|
-
"dependsOn": ["build"],
|
|
30
|
-
"executor": "nx:run-commands",
|
|
31
|
-
"outputs": [],
|
|
32
|
-
"options": {
|
|
33
|
-
"commands": [
|
|
34
|
-
"rsync -rat packages/hooks/*.md build/packages/vctrl/hooks/"
|
|
35
|
-
]
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
"build": {
|
|
39
|
-
"executor": "@nx/vite:build",
|
|
40
|
-
"outputs": ["{options.outputPath}"],
|
|
41
|
-
"options": {
|
|
42
|
-
"outputPath": "build/packages/vctrl/hooks",
|
|
43
|
-
"configFile": "packages/hooks/vite.config.ts"
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
"build-ci": {
|
|
47
|
-
"executor": "@nx/vite:build",
|
|
48
|
-
"outputs": ["{options.outputPath}"],
|
|
49
|
-
"options": {
|
|
50
|
-
"outputPath": "build/packages/vctrl/hooks",
|
|
51
|
-
"configFile": "packages/hooks/vite.config.ts"
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|