brep-io-kernel 1.0.0-ci.9
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/LICENSE.md +32 -0
- package/README.md +154 -0
- package/dist-kernel/brep-kernel.js +74699 -0
- package/package.json +58 -0
- package/src/BREP/AssemblyComponent.js +42 -0
- package/src/BREP/BREP.js +43 -0
- package/src/BREP/BetterSolid.js +805 -0
- package/src/BREP/Edge.js +103 -0
- package/src/BREP/Extrude.js +403 -0
- package/src/BREP/Face.js +187 -0
- package/src/BREP/MeshRepairer.js +634 -0
- package/src/BREP/OffsetShellSolid.js +614 -0
- package/src/BREP/PointCloudWrap.js +302 -0
- package/src/BREP/Revolve.js +345 -0
- package/src/BREP/SolidMethods/authoring.js +112 -0
- package/src/BREP/SolidMethods/booleanOps.js +230 -0
- package/src/BREP/SolidMethods/chamfer.js +122 -0
- package/src/BREP/SolidMethods/edgeResolution.js +25 -0
- package/src/BREP/SolidMethods/fillet.js +792 -0
- package/src/BREP/SolidMethods/index.js +72 -0
- package/src/BREP/SolidMethods/io.js +105 -0
- package/src/BREP/SolidMethods/lifecycle.js +103 -0
- package/src/BREP/SolidMethods/manifoldOps.js +375 -0
- package/src/BREP/SolidMethods/meshCleanup.js +2512 -0
- package/src/BREP/SolidMethods/meshQueries.js +264 -0
- package/src/BREP/SolidMethods/metadata.js +106 -0
- package/src/BREP/SolidMethods/metrics.js +51 -0
- package/src/BREP/SolidMethods/transforms.js +361 -0
- package/src/BREP/SolidMethods/visualize.js +508 -0
- package/src/BREP/SolidShared.js +26 -0
- package/src/BREP/Sweep.js +1596 -0
- package/src/BREP/Tube.js +857 -0
- package/src/BREP/Vertex.js +43 -0
- package/src/BREP/applyBooleanOperation.js +704 -0
- package/src/BREP/boundsUtils.js +48 -0
- package/src/BREP/chamfer.js +551 -0
- package/src/BREP/edgePolylineUtils.js +85 -0
- package/src/BREP/fillets/common.js +388 -0
- package/src/BREP/fillets/fillet.js +1422 -0
- package/src/BREP/fillets/filletGeometry.js +15 -0
- package/src/BREP/fillets/inset.js +389 -0
- package/src/BREP/fillets/offsetHelper.js +143 -0
- package/src/BREP/fillets/outset.js +88 -0
- package/src/BREP/helix.js +193 -0
- package/src/BREP/meshToBrep.js +234 -0
- package/src/BREP/primitives.js +279 -0
- package/src/BREP/setupManifold.js +71 -0
- package/src/BREP/threadGeometry.js +1120 -0
- package/src/BREP/triangleUtils.js +8 -0
- package/src/BREP/triangulate.js +608 -0
- package/src/FeatureRegistry.js +183 -0
- package/src/PartHistory.js +1132 -0
- package/src/UI/AccordionWidget.js +292 -0
- package/src/UI/CADmaterials.js +850 -0
- package/src/UI/EnvMonacoEditor.js +522 -0
- package/src/UI/FloatingWindow.js +396 -0
- package/src/UI/HistoryWidget.js +457 -0
- package/src/UI/MainToolbar.js +131 -0
- package/src/UI/ModelLibraryView.js +194 -0
- package/src/UI/OrthoCameraIdle.js +206 -0
- package/src/UI/PluginsWidget.js +280 -0
- package/src/UI/SceneListing.js +606 -0
- package/src/UI/SelectionFilter.js +629 -0
- package/src/UI/ViewCube.js +389 -0
- package/src/UI/assembly/AssemblyConstraintCollectionWidget.js +329 -0
- package/src/UI/assembly/AssemblyConstraintControlsWidget.js +282 -0
- package/src/UI/assembly/AssemblyConstraintsWidget.css +292 -0
- package/src/UI/assembly/AssemblyConstraintsWidget.js +1373 -0
- package/src/UI/assembly/constraintFaceUtils.js +115 -0
- package/src/UI/assembly/constraintHighlightUtils.js +70 -0
- package/src/UI/assembly/constraintLabelUtils.js +31 -0
- package/src/UI/assembly/constraintPointUtils.js +64 -0
- package/src/UI/assembly/constraintSelectionUtils.js +185 -0
- package/src/UI/assembly/constraintStatusUtils.js +142 -0
- package/src/UI/componentSelectorModal.js +240 -0
- package/src/UI/controls/CombinedTransformControls.js +386 -0
- package/src/UI/dialogs.js +351 -0
- package/src/UI/expressionsManager.js +100 -0
- package/src/UI/featureDialogWidgets/booleanField.js +25 -0
- package/src/UI/featureDialogWidgets/booleanOperationField.js +97 -0
- package/src/UI/featureDialogWidgets/buttonField.js +45 -0
- package/src/UI/featureDialogWidgets/componentSelectorField.js +102 -0
- package/src/UI/featureDialogWidgets/defaultField.js +23 -0
- package/src/UI/featureDialogWidgets/fileField.js +66 -0
- package/src/UI/featureDialogWidgets/index.js +34 -0
- package/src/UI/featureDialogWidgets/numberField.js +165 -0
- package/src/UI/featureDialogWidgets/optionsField.js +33 -0
- package/src/UI/featureDialogWidgets/referenceSelectionField.js +208 -0
- package/src/UI/featureDialogWidgets/stringField.js +24 -0
- package/src/UI/featureDialogWidgets/textareaField.js +28 -0
- package/src/UI/featureDialogWidgets/threadDesignationField.js +160 -0
- package/src/UI/featureDialogWidgets/transformField.js +252 -0
- package/src/UI/featureDialogWidgets/utils.js +43 -0
- package/src/UI/featureDialogWidgets/vec3Field.js +133 -0
- package/src/UI/featureDialogs.js +1414 -0
- package/src/UI/fileManagerWidget.js +615 -0
- package/src/UI/history/HistoryCollectionWidget.js +1294 -0
- package/src/UI/history/historyCollectionWidget.css.js +257 -0
- package/src/UI/history/historyDisplayInfo.js +133 -0
- package/src/UI/mobile.js +28 -0
- package/src/UI/objectDump.js +442 -0
- package/src/UI/pmi/AnnotationCollectionWidget.js +120 -0
- package/src/UI/pmi/AnnotationHistory.js +353 -0
- package/src/UI/pmi/AnnotationRegistry.js +90 -0
- package/src/UI/pmi/BaseAnnotation.js +269 -0
- package/src/UI/pmi/LabelOverlay.css +102 -0
- package/src/UI/pmi/LabelOverlay.js +191 -0
- package/src/UI/pmi/PMIMode.js +1550 -0
- package/src/UI/pmi/PMIViewsWidget.js +1098 -0
- package/src/UI/pmi/annUtils.js +729 -0
- package/src/UI/pmi/dimensions/AngleDimensionAnnotation.js +647 -0
- package/src/UI/pmi/dimensions/ExplodeBodyAnnotation.js +507 -0
- package/src/UI/pmi/dimensions/HoleCalloutAnnotation.js +462 -0
- package/src/UI/pmi/dimensions/LeaderAnnotation.js +403 -0
- package/src/UI/pmi/dimensions/LinearDimensionAnnotation.js +532 -0
- package/src/UI/pmi/dimensions/NoteAnnotation.js +110 -0
- package/src/UI/pmi/dimensions/RadialDimensionAnnotation.js +659 -0
- package/src/UI/pmi/pmiStyle.js +44 -0
- package/src/UI/sketcher/SketchMode3D.js +4095 -0
- package/src/UI/sketcher/dimensions.js +674 -0
- package/src/UI/sketcher/glyphs.js +236 -0
- package/src/UI/sketcher/highlights.js +60 -0
- package/src/UI/toolbarButtons/aboutButton.js +5 -0
- package/src/UI/toolbarButtons/exportButton.js +609 -0
- package/src/UI/toolbarButtons/flatPatternButton.js +307 -0
- package/src/UI/toolbarButtons/importButton.js +160 -0
- package/src/UI/toolbarButtons/inspectorToggleButton.js +12 -0
- package/src/UI/toolbarButtons/metadataButton.js +1063 -0
- package/src/UI/toolbarButtons/orientToFaceButton.js +114 -0
- package/src/UI/toolbarButtons/registerDefaultButtons.js +46 -0
- package/src/UI/toolbarButtons/saveButton.js +99 -0
- package/src/UI/toolbarButtons/scriptRunnerButton.js +302 -0
- package/src/UI/toolbarButtons/testsButton.js +26 -0
- package/src/UI/toolbarButtons/undoRedoButtons.js +25 -0
- package/src/UI/toolbarButtons/wireframeToggleButton.js +5 -0
- package/src/UI/toolbarButtons/zoomToFitButton.js +5 -0
- package/src/UI/triangleDebuggerWindow.js +945 -0
- package/src/UI/viewer.js +4228 -0
- package/src/assemblyConstraints/AssemblyConstraintHistory.js +1576 -0
- package/src/assemblyConstraints/AssemblyConstraintRegistry.js +120 -0
- package/src/assemblyConstraints/BaseAssemblyConstraint.js +66 -0
- package/src/assemblyConstraints/constraintExpressionUtils.js +35 -0
- package/src/assemblyConstraints/constraintUtils/parallelAlignment.js +676 -0
- package/src/assemblyConstraints/constraints/AngleConstraint.js +485 -0
- package/src/assemblyConstraints/constraints/CoincidentConstraint.js +194 -0
- package/src/assemblyConstraints/constraints/DistanceConstraint.js +616 -0
- package/src/assemblyConstraints/constraints/FixedConstraint.js +78 -0
- package/src/assemblyConstraints/constraints/ParallelConstraint.js +252 -0
- package/src/assemblyConstraints/constraints/TouchAlignConstraint.js +961 -0
- package/src/core/entities/HistoryCollectionBase.js +72 -0
- package/src/core/entities/ListEntityBase.js +109 -0
- package/src/core/entities/schemaProcesser.js +121 -0
- package/src/exporters/sheetMetalFlatPattern.js +659 -0
- package/src/exporters/sheetMetalUnfold.js +862 -0
- package/src/exporters/step.js +1135 -0
- package/src/exporters/threeMF.js +575 -0
- package/src/features/assemblyComponent/AssemblyComponentFeature.js +780 -0
- package/src/features/boolean/BooleanFeature.js +94 -0
- package/src/features/chamfer/ChamferFeature.js +116 -0
- package/src/features/datium/DatiumFeature.js +80 -0
- package/src/features/edgeFeatureUtils.js +41 -0
- package/src/features/extrude/ExtrudeFeature.js +143 -0
- package/src/features/fillet/FilletFeature.js +197 -0
- package/src/features/helix/HelixFeature.js +405 -0
- package/src/features/hole/HoleFeature.js +1050 -0
- package/src/features/hole/screwClearance.js +86 -0
- package/src/features/hole/threadDesignationCatalog.js +149 -0
- package/src/features/imageHeightSolid/ImageHeightmapSolidFeature.js +463 -0
- package/src/features/imageToFace/ImageToFaceFeature.js +727 -0
- package/src/features/imageToFace/imageEditor.js +1270 -0
- package/src/features/imageToFace/traceUtils.js +971 -0
- package/src/features/import3dModel/Import3dModelFeature.js +151 -0
- package/src/features/loft/LoftFeature.js +605 -0
- package/src/features/mirror/MirrorFeature.js +151 -0
- package/src/features/offsetFace/OffsetFaceFeature.js +370 -0
- package/src/features/offsetShell/OffsetShellFeature.js +89 -0
- package/src/features/overlapCleanup/OverlapCleanupFeature.js +85 -0
- package/src/features/pattern/PatternFeature.js +275 -0
- package/src/features/patternLinear/PatternLinearFeature.js +120 -0
- package/src/features/patternRadial/PatternRadialFeature.js +186 -0
- package/src/features/plane/PlaneFeature.js +154 -0
- package/src/features/primitiveCone/primitiveConeFeature.js +99 -0
- package/src/features/primitiveCube/primitiveCubeFeature.js +70 -0
- package/src/features/primitiveCylinder/primitiveCylinderFeature.js +91 -0
- package/src/features/primitivePyramid/primitivePyramidFeature.js +72 -0
- package/src/features/primitiveSphere/primitiveSphereFeature.js +62 -0
- package/src/features/primitiveTorus/primitiveTorusFeature.js +109 -0
- package/src/features/remesh/RemeshFeature.js +97 -0
- package/src/features/revolve/RevolveFeature.js +111 -0
- package/src/features/selectionUtils.js +118 -0
- package/src/features/sheetMetal/SheetMetalContourFlangeFeature.js +1656 -0
- package/src/features/sheetMetal/SheetMetalCutoutFeature.js +1056 -0
- package/src/features/sheetMetal/SheetMetalFlangeFeature.js +1568 -0
- package/src/features/sheetMetal/SheetMetalHemFeature.js +43 -0
- package/src/features/sheetMetal/SheetMetalObject.js +141 -0
- package/src/features/sheetMetal/SheetMetalTabFeature.js +176 -0
- package/src/features/sheetMetal/UNFOLD_NEUTRAL_REQUIREMENTS.md +153 -0
- package/src/features/sheetMetal/contour-flange-rebuild-spec.md +261 -0
- package/src/features/sheetMetal/profileUtils.js +25 -0
- package/src/features/sheetMetal/sheetMetalCleanup.js +9 -0
- package/src/features/sheetMetal/sheetMetalFaceTypes.js +146 -0
- package/src/features/sheetMetal/sheetMetalMetadata.js +165 -0
- package/src/features/sheetMetal/sheetMetalPipeline.js +169 -0
- package/src/features/sheetMetal/sheetMetalProfileUtils.js +216 -0
- package/src/features/sheetMetal/sheetMetalTabUtils.js +29 -0
- package/src/features/sheetMetal/sheetMetalTree.js +210 -0
- package/src/features/sketch/SketchFeature.js +955 -0
- package/src/features/sketch/sketchSolver2D/ConstraintEngine.js +800 -0
- package/src/features/sketch/sketchSolver2D/constraintDefinitions.js +704 -0
- package/src/features/sketch/sketchSolver2D/mathHelpersMod.js +307 -0
- package/src/features/spline/SplineEditorSession.js +988 -0
- package/src/features/spline/SplineFeature.js +1388 -0
- package/src/features/spline/splineUtils.js +218 -0
- package/src/features/sweep/SweepFeature.js +110 -0
- package/src/features/transform/TransformFeature.js +152 -0
- package/src/features/tube/TubeFeature.js +635 -0
- package/src/fs.proxy.js +625 -0
- package/src/idbStorage.js +254 -0
- package/src/index.js +12 -0
- package/src/main.js +15 -0
- package/src/metadataManager.js +64 -0
- package/src/path.proxy.js +277 -0
- package/src/plugins/ghLoader.worker.js +151 -0
- package/src/plugins/pluginManager.js +286 -0
- package/src/pmi/PMIViewsManager.js +134 -0
- package/src/services/componentLibrary.js +198 -0
- package/src/tests/ConsoleCapture.js +189 -0
- package/src/tests/S7-diagnostics-2025-12-23T18-37-23-570Z.json +630 -0
- package/src/tests/browserTests.js +597 -0
- package/src/tests/debugBoolean.js +225 -0
- package/src/tests/partFiles/badBoolean.json +957 -0
- package/src/tests/partFiles/extrudeTest.json +88 -0
- package/src/tests/partFiles/filletFail.json +58 -0
- package/src/tests/partFiles/import_TEst.part.part.json +646 -0
- package/src/tests/partFiles/sheetMetalHem.BREP.json +734 -0
- package/src/tests/test_boolean_subtract.js +27 -0
- package/src/tests/test_chamfer.js +17 -0
- package/src/tests/test_extrudeFace.js +24 -0
- package/src/tests/test_fillet.js +17 -0
- package/src/tests/test_fillet_nonClosed.js +45 -0
- package/src/tests/test_filletsMoreDifficult.js +46 -0
- package/src/tests/test_history_features_basic.js +149 -0
- package/src/tests/test_hole.js +282 -0
- package/src/tests/test_mirror.js +16 -0
- package/src/tests/test_offsetShellGrouping.js +85 -0
- package/src/tests/test_plane.js +4 -0
- package/src/tests/test_primitiveCone.js +11 -0
- package/src/tests/test_primitiveCube.js +7 -0
- package/src/tests/test_primitiveCylinder.js +8 -0
- package/src/tests/test_primitivePyramid.js +9 -0
- package/src/tests/test_primitiveSphere.js +17 -0
- package/src/tests/test_primitiveTorus.js +21 -0
- package/src/tests/test_pushFace.js +126 -0
- package/src/tests/test_sheetMetalContourFlange.js +125 -0
- package/src/tests/test_sheetMetal_features.js +80 -0
- package/src/tests/test_sketch_openLoop.js +45 -0
- package/src/tests/test_solidMetrics.js +58 -0
- package/src/tests/test_stlLoader.js +1889 -0
- package/src/tests/test_sweepFace.js +55 -0
- package/src/tests/test_tube.js +45 -0
- package/src/tests/test_tube_closedLoop.js +67 -0
- package/src/tests/tests.js +493 -0
- package/src/tools/assemblyConstraintDialogCapturePage.js +56 -0
- package/src/tools/dialogCapturePageFactory.js +227 -0
- package/src/tools/featureDialogCapturePage.js +47 -0
- package/src/tools/pmiAnnotationDialogCapturePage.js +60 -0
- package/src/utils/axisHelpers.js +99 -0
- package/src/utils/deepClone.js +69 -0
- package/src/utils/geometryTolerance.js +37 -0
- package/src/utils/normalizeTypeString.js +8 -0
- package/src/utils/xformMath.js +51 -0
package/src/fs.proxy.js
ADDED
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
// fs.proxy.js - ESM-safe, works in Node (CJS & ESM) and browser.
|
|
2
|
+
// - Node: proxies native fs (sync, callback, and promises).
|
|
3
|
+
// - Browser: IndexedDB-backed VFS for a common subset.
|
|
4
|
+
|
|
5
|
+
const isNode =
|
|
6
|
+
typeof process !== 'undefined' &&
|
|
7
|
+
process.versions &&
|
|
8
|
+
process.versions.node &&
|
|
9
|
+
typeof window === 'undefined';
|
|
10
|
+
// Node ESM: preload sync fs once via top-level await.
|
|
11
|
+
// Node ESM: preload sync fs once WITHOUT top-level await (safe for browsers).
|
|
12
|
+
let nodeFsSync = null;
|
|
13
|
+
let _preloadFsStarted = false;
|
|
14
|
+
|
|
15
|
+
function _kickoffNodeFsPreload() {
|
|
16
|
+
if (!isNode || _preloadFsStarted) return;
|
|
17
|
+
_preloadFsStarted = true;
|
|
18
|
+
(async () => {
|
|
19
|
+
try {
|
|
20
|
+
const fsMod = await import('node:fs');
|
|
21
|
+
nodeFsSync = fsMod.default ?? fsMod;
|
|
22
|
+
} catch (_) {
|
|
23
|
+
nodeFsSync = null;
|
|
24
|
+
}
|
|
25
|
+
})();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// start the preload in Node, noop in browsers
|
|
29
|
+
_kickoffNodeFsPreload();
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
let nodeFs = null;
|
|
33
|
+
let nodeFsPromises = null;
|
|
34
|
+
|
|
35
|
+
// Lazily load async fs in Node
|
|
36
|
+
async function loadNodeFsIfNeeded() {
|
|
37
|
+
if (!isNode) return;
|
|
38
|
+
if (!nodeFs) {
|
|
39
|
+
const fsMod = await import('node:fs');
|
|
40
|
+
const fsPromisesMod = await import('node:fs/promises');
|
|
41
|
+
nodeFs = fsMod.default ?? fsMod;
|
|
42
|
+
nodeFsPromises = fsPromisesMod.default ?? fsPromisesMod;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// -------------------- Browser VFS --------------------
|
|
47
|
+
|
|
48
|
+
const hasIndexedDB = typeof indexedDB !== 'undefined' && !!indexedDB.open;
|
|
49
|
+
const VFS_DB_NAME = '__BREP_VFS_DB__';
|
|
50
|
+
const VFS_STORE_NAME = 'vfs';
|
|
51
|
+
const VFS_DB_VERSION = 1;
|
|
52
|
+
const VFS_KEY = '__VFS_INDEX__';
|
|
53
|
+
|
|
54
|
+
function promisifyRequest(req) {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
req.onsuccess = () => resolve(req.result);
|
|
57
|
+
req.onerror = () => reject(req.error || new Error('IDB request failed'));
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function openVfsDB() {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const openReq = indexedDB.open(VFS_DB_NAME, VFS_DB_VERSION);
|
|
64
|
+
openReq.onupgradeneeded = () => {
|
|
65
|
+
const db = openReq.result;
|
|
66
|
+
if (!db.objectStoreNames.contains(VFS_STORE_NAME)) {
|
|
67
|
+
db.createObjectStore(VFS_STORE_NAME);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
openReq.onsuccess = () => resolve(openReq.result);
|
|
71
|
+
openReq.onerror = () => reject(openReq.error || new Error('Failed to open VFS DB'));
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function idbGet(db, key) {
|
|
76
|
+
const tx = db.transaction([VFS_STORE_NAME], 'readonly');
|
|
77
|
+
const store = tx.objectStore(VFS_STORE_NAME);
|
|
78
|
+
return promisifyRequest(store.get(key));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function idbPut(db, key, value) {
|
|
82
|
+
const tx = db.transaction([VFS_STORE_NAME], 'readwrite');
|
|
83
|
+
tx.objectStore(VFS_STORE_NAME).put(value, key);
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
tx.oncomplete = () => resolve();
|
|
86
|
+
tx.onerror = () => reject(tx.error || new Error('IDB put failed'));
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function coerceIndex(raw) {
|
|
91
|
+
if (!raw) return null;
|
|
92
|
+
let parsed = raw;
|
|
93
|
+
if (typeof raw === 'string') {
|
|
94
|
+
try {
|
|
95
|
+
parsed = JSON.parse(raw);
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!parsed || typeof parsed !== 'object' || typeof parsed.entries !== 'object') return null;
|
|
101
|
+
return parsed;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
class Stats {
|
|
105
|
+
constructor(entry) {
|
|
106
|
+
this.dev = 0;
|
|
107
|
+
this.mode = entry?.mode ?? 0o666;
|
|
108
|
+
this.nlink = 1;
|
|
109
|
+
this.uid = 0;
|
|
110
|
+
this.gid = 0;
|
|
111
|
+
this.rdev = 0;
|
|
112
|
+
this.blksize = 4096;
|
|
113
|
+
this.ino = 0;
|
|
114
|
+
this.size = entry?.size ?? 0;
|
|
115
|
+
this.blocks = Math.ceil(this.size / this.blksize);
|
|
116
|
+
this.atimeMs = entry?.mtimeMs ?? Date.now();
|
|
117
|
+
this.mtimeMs = entry?.mtimeMs ?? Date.now();
|
|
118
|
+
this.ctimeMs = entry?.mtimeMs ?? Date.now();
|
|
119
|
+
this.birthtimeMs = entry?.birthtimeMs ?? this.mtimeMs;
|
|
120
|
+
|
|
121
|
+
this.atime = new Date(this.atimeMs);
|
|
122
|
+
this.mtime = new Date(this.mtimeMs);
|
|
123
|
+
this.ctime = new Date(this.ctimeMs);
|
|
124
|
+
this.birthtime = new Date(this.birthtimeMs);
|
|
125
|
+
|
|
126
|
+
this._type = entry?.type ?? 'file';
|
|
127
|
+
}
|
|
128
|
+
isFile() { return this._type === 'file'; }
|
|
129
|
+
isDirectory() { return this._type === 'dir'; }
|
|
130
|
+
isSymbolicLink() { return false; }
|
|
131
|
+
isFIFO() { return false; }
|
|
132
|
+
isSocket() { return false; }
|
|
133
|
+
isCharacterDevice() { return false; }
|
|
134
|
+
isBlockDevice() { return false; }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function normalizePath(p) {
|
|
138
|
+
if (!p) return '/';
|
|
139
|
+
p = p.replace(/\\/g, '/');
|
|
140
|
+
if (!p.startsWith('/')) p = '/' + p;
|
|
141
|
+
const parts = [];
|
|
142
|
+
for (const seg of p.split('/')) {
|
|
143
|
+
if (!seg || seg === '.') continue;
|
|
144
|
+
if (seg === '..') parts.pop();
|
|
145
|
+
else parts.push(seg);
|
|
146
|
+
}
|
|
147
|
+
return '/' + parts.join('/');
|
|
148
|
+
}
|
|
149
|
+
function dirname(p) {
|
|
150
|
+
p = normalizePath(p);
|
|
151
|
+
const parts = p.split('/');
|
|
152
|
+
parts.pop();
|
|
153
|
+
if (parts.length === 1) return '/';
|
|
154
|
+
return parts.join('/') || '/';
|
|
155
|
+
}
|
|
156
|
+
function basename(p) {
|
|
157
|
+
p = normalizePath(p);
|
|
158
|
+
const parts = p.split('/');
|
|
159
|
+
return parts.pop() || '/';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function strToUint8(str) { return new TextEncoder().encode(str); }
|
|
163
|
+
function uint8ToStr(uint8) { return new TextDecoder().decode(uint8); }
|
|
164
|
+
function uint8ToBase64(uint8) {
|
|
165
|
+
let binary = '';
|
|
166
|
+
const chunk = 0x8000;
|
|
167
|
+
for (let i = 0; i < uint8.length; i += chunk) {
|
|
168
|
+
const sub = uint8.subarray(i, i + chunk);
|
|
169
|
+
binary += String.fromCharCode.apply(null, sub);
|
|
170
|
+
}
|
|
171
|
+
return btoa(binary);
|
|
172
|
+
}
|
|
173
|
+
function base64ToUint8(b64) {
|
|
174
|
+
const binary = atob(b64);
|
|
175
|
+
const out = new Uint8Array(binary.length);
|
|
176
|
+
for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
function nowMs() { return Date.now(); }
|
|
180
|
+
|
|
181
|
+
class IndexedDbFS {
|
|
182
|
+
constructor() {
|
|
183
|
+
this.index = { entries: {} };
|
|
184
|
+
this._idbEnabled = hasIndexedDB;
|
|
185
|
+
this._ready = false;
|
|
186
|
+
this._dbPromise = null;
|
|
187
|
+
this._persistChain = Promise.resolve();
|
|
188
|
+
this._initPromise = this._init();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async _init() {
|
|
192
|
+
if (!this._idbEnabled) {
|
|
193
|
+
this._ensureIndex();
|
|
194
|
+
this._ready = true;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this._dbPromise = openVfsDB();
|
|
199
|
+
try {
|
|
200
|
+
const db = await this._dbPromise;
|
|
201
|
+
const stored = coerceIndex(await idbGet(db, VFS_KEY));
|
|
202
|
+
if (stored) {
|
|
203
|
+
this.index = stored;
|
|
204
|
+
}
|
|
205
|
+
} catch (err) {
|
|
206
|
+
console.warn('[vfs] IndexedDB unavailable; using in-memory storage only.', err);
|
|
207
|
+
this._idbEnabled = false;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
this._ensureIndex();
|
|
211
|
+
this._ready = true;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
ready() { return this._initPromise; }
|
|
215
|
+
|
|
216
|
+
_assertReady() {
|
|
217
|
+
if (!this._ready) {
|
|
218
|
+
throw new Error('Browser VFS not ready. Await fs.ready() before using sync APIs.');
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
_ensureIndex() {
|
|
223
|
+
if (!this.index || typeof this.index !== 'object' || typeof this.index.entries !== 'object') {
|
|
224
|
+
this.index = { entries: {} };
|
|
225
|
+
}
|
|
226
|
+
if (!this.index.entries['/']) {
|
|
227
|
+
this.index.entries['/'] = { type: 'dir', children: [], mtimeMs: nowMs(), mode: 0o777 };
|
|
228
|
+
this._save();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
_save() {
|
|
233
|
+
if (!this._idbEnabled) return;
|
|
234
|
+
if (!this._dbPromise) this._dbPromise = openVfsDB();
|
|
235
|
+
const snapshot = this.index;
|
|
236
|
+
this._persistChain = this._persistChain
|
|
237
|
+
.then(() => this._dbPromise)
|
|
238
|
+
.then((db) => idbPut(db, VFS_KEY, snapshot))
|
|
239
|
+
.catch((err) => {
|
|
240
|
+
console.warn('[vfs] Persist failed:', err);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
_enoent(code, path) { const err = new Error(`${code}: no such file or directory, ${path}`); err.code = code; return err; }
|
|
244
|
+
_eexist(code, path) { const err = new Error(`${code}: file already exists, ${path}`); err.code = code; return err; }
|
|
245
|
+
_linkIntoParent(p) {
|
|
246
|
+
const parent = dirname(p);
|
|
247
|
+
const name = basename(p);
|
|
248
|
+
const eParent = this.index.entries[parent];
|
|
249
|
+
if (eParent && eParent.type === 'dir') {
|
|
250
|
+
if (!eParent.children.includes(name)) {
|
|
251
|
+
eParent.children.push(name);
|
|
252
|
+
eParent.mtimeMs = nowMs();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
_unlinkFromParent(p) {
|
|
257
|
+
const parent = dirname(p);
|
|
258
|
+
const name = basename(p);
|
|
259
|
+
const eParent = this.index.entries[parent];
|
|
260
|
+
if (eParent && eParent.type === 'dir') {
|
|
261
|
+
const i = eParent.children.indexOf(name);
|
|
262
|
+
if (i >= 0) {
|
|
263
|
+
eParent.children.splice(i, 1);
|
|
264
|
+
eParent.mtimeMs = nowMs();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
_resolveEncoding(options) {
|
|
269
|
+
if (!options) return null;
|
|
270
|
+
if (typeof options === 'string') return options;
|
|
271
|
+
if (typeof options === 'object' && options.encoding) return options.encoding;
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
writeFileSync(path, data, options = {}) {
|
|
276
|
+
this._assertReady();
|
|
277
|
+
path = normalizePath(path);
|
|
278
|
+
const enc = this._resolveEncoding(options);
|
|
279
|
+
const parent = dirname(path);
|
|
280
|
+
const eParent = this.index.entries[parent];
|
|
281
|
+
if (!eParent || eParent.type !== 'dir') throw this._enoent('ENOENT', parent);
|
|
282
|
+
|
|
283
|
+
let bytes;
|
|
284
|
+
if (data instanceof Uint8Array) bytes = data;
|
|
285
|
+
else if (typeof data === 'string') {
|
|
286
|
+
if (enc && enc !== 'utf8') throw new Error(`Unsupported string encoding in browser VFS: ${enc}`);
|
|
287
|
+
bytes = strToUint8(data);
|
|
288
|
+
} else if (ArrayBuffer.isView(data)) {
|
|
289
|
+
bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
290
|
+
} else if (data instanceof ArrayBuffer) {
|
|
291
|
+
bytes = new Uint8Array(data);
|
|
292
|
+
} else {
|
|
293
|
+
throw new Error('Unsupported data type for writeFileSync');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const content = uint8ToBase64(bytes);
|
|
297
|
+
const exists = !!this.index.entries[path];
|
|
298
|
+
this.index.entries[path] = {
|
|
299
|
+
type: 'file',
|
|
300
|
+
data: content,
|
|
301
|
+
encoding: 'base64',
|
|
302
|
+
size: bytes.length,
|
|
303
|
+
mtimeMs: nowMs(),
|
|
304
|
+
mode: (options.mode ?? 0o666)
|
|
305
|
+
};
|
|
306
|
+
if (!exists) this._linkIntoParent(path);
|
|
307
|
+
this._save();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
appendFileSync(path, data, options = {}) {
|
|
311
|
+
this._assertReady();
|
|
312
|
+
path = normalizePath(path);
|
|
313
|
+
const enc = this._resolveEncoding(options);
|
|
314
|
+
|
|
315
|
+
let appendBytes;
|
|
316
|
+
if (data instanceof Uint8Array) appendBytes = data;
|
|
317
|
+
else if (typeof data === 'string') {
|
|
318
|
+
if (enc && enc !== 'utf8') throw new Error(`Unsupported string encoding in browser VFS: ${enc}`);
|
|
319
|
+
appendBytes = strToUint8(data);
|
|
320
|
+
} else if (ArrayBuffer.isView(data)) {
|
|
321
|
+
appendBytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
322
|
+
} else if (data instanceof ArrayBuffer) {
|
|
323
|
+
appendBytes = new Uint8Array(data);
|
|
324
|
+
} else {
|
|
325
|
+
throw new Error('Unsupported data type for appendFileSync');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const existing = this.index.entries[path];
|
|
329
|
+
if (!existing) return this.writeFileSync(path, appendBytes, options);
|
|
330
|
+
if (existing.type !== 'file') throw this._enoent('EISDIR', path);
|
|
331
|
+
|
|
332
|
+
const current = base64ToUint8(existing.data);
|
|
333
|
+
const merged = new Uint8Array(current.length + appendBytes.length);
|
|
334
|
+
merged.set(current, 0);
|
|
335
|
+
merged.set(appendBytes, current.length);
|
|
336
|
+
|
|
337
|
+
existing.data = uint8ToBase64(merged);
|
|
338
|
+
existing.size = merged.length;
|
|
339
|
+
existing.mtimeMs = nowMs();
|
|
340
|
+
this._save();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
readFileSync(path, options = {}) {
|
|
344
|
+
this._assertReady();
|
|
345
|
+
path = normalizePath(path);
|
|
346
|
+
const entry = this.index.entries[path];
|
|
347
|
+
if (!entry || entry.type !== 'file') throw this._enoent('ENOENT', path);
|
|
348
|
+
|
|
349
|
+
const bytes = base64ToUint8(entry.data);
|
|
350
|
+
const enc = this._resolveEncoding(options);
|
|
351
|
+
if (enc) {
|
|
352
|
+
if (enc !== 'utf8') throw new Error(`Unsupported encoding in browser VFS: ${enc}`);
|
|
353
|
+
return uint8ToStr(bytes);
|
|
354
|
+
}
|
|
355
|
+
return bytes;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
mkdirSync(path, options = {}) {
|
|
359
|
+
this._assertReady();
|
|
360
|
+
path = normalizePath(path);
|
|
361
|
+
const recursive = !!(options && (options.recursive === true));
|
|
362
|
+
if (this.index.entries[path]) {
|
|
363
|
+
if (this.index.entries[path].type === 'dir') return;
|
|
364
|
+
throw this._eexist('EEXIST', path);
|
|
365
|
+
}
|
|
366
|
+
if (!recursive) {
|
|
367
|
+
const parent = dirname(path);
|
|
368
|
+
const eParent = this.index.entries[parent];
|
|
369
|
+
if (!eParent || eParent.type !== 'dir') throw this._enoent('ENOENT', parent);
|
|
370
|
+
this.index.entries[path] = { type: 'dir', children: [], mtimeMs: nowMs(), mode: options.mode ?? 0o777 };
|
|
371
|
+
this._linkIntoParent(path); this._save(); return;
|
|
372
|
+
}
|
|
373
|
+
const parts = normalizePath(path).split('/').filter(Boolean);
|
|
374
|
+
let cur = '/';
|
|
375
|
+
for (const part of parts) {
|
|
376
|
+
const next = normalizePath(cur + '/' + part);
|
|
377
|
+
if (!this.index.entries[next]) {
|
|
378
|
+
this.index.entries[next] = { type: 'dir', children: [], mtimeMs: nowMs(), mode: 0o777 };
|
|
379
|
+
this._linkIntoParent(next);
|
|
380
|
+
} else if (this.index.entries[next].type !== 'dir') {
|
|
381
|
+
throw this._eexist('EEXIST', next);
|
|
382
|
+
}
|
|
383
|
+
cur = next;
|
|
384
|
+
}
|
|
385
|
+
this._save();
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
readdirSync(path, options = {}) {
|
|
389
|
+
this._assertReady();
|
|
390
|
+
path = normalizePath(path);
|
|
391
|
+
const entry = this.index.entries[path];
|
|
392
|
+
if (!entry || entry.type !== 'dir') throw this._enoent('ENOTDIR', path);
|
|
393
|
+
const withFileTypes = !!options.withFileTypes;
|
|
394
|
+
const list = entry.children.slice();
|
|
395
|
+
if (!withFileTypes) return list;
|
|
396
|
+
return list.map(name => {
|
|
397
|
+
const childPath = normalizePath(path + '/' + name);
|
|
398
|
+
const ch = this.index.entries[childPath];
|
|
399
|
+
return {
|
|
400
|
+
name,
|
|
401
|
+
isFile: () => ch?.type === 'file',
|
|
402
|
+
isDirectory: () => ch?.type === 'dir'
|
|
403
|
+
};
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
unlinkSync(path) {
|
|
408
|
+
this._assertReady();
|
|
409
|
+
path = normalizePath(path);
|
|
410
|
+
const entry = this.index.entries[path];
|
|
411
|
+
if (!entry) throw this._enoent('ENOENT', path);
|
|
412
|
+
if (entry.type !== 'file') throw this._enoent('EISDIR', path);
|
|
413
|
+
delete this.index.entries[path];
|
|
414
|
+
this._unlinkFromParent(path);
|
|
415
|
+
this._save();
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
rmdirSync(path, options = {}) {
|
|
419
|
+
this._assertReady();
|
|
420
|
+
path = normalizePath(path);
|
|
421
|
+
const entry = this.index.entries[path];
|
|
422
|
+
if (!entry) throw this._enoent('ENOENT', path);
|
|
423
|
+
if (entry.type !== 'dir') throw this._enoent('ENOTDIR', path);
|
|
424
|
+
const recursive = !!options.recursive;
|
|
425
|
+
if (entry.children.length && !recursive) {
|
|
426
|
+
const err = new Error(`ENOTEMPTY: directory not empty, ${path}`); err.code = 'ENOTEMPTY'; throw err;
|
|
427
|
+
}
|
|
428
|
+
if (recursive) {
|
|
429
|
+
const stack = [path];
|
|
430
|
+
while (stack.length) {
|
|
431
|
+
const cur = stack.pop();
|
|
432
|
+
const e = this.index.entries[cur];
|
|
433
|
+
if (!e) continue;
|
|
434
|
+
if (e.type === 'dir') {
|
|
435
|
+
for (const name of e.children) stack.push(normalizePath(cur + '/' + name));
|
|
436
|
+
}
|
|
437
|
+
if (cur !== path) delete this.index.entries[cur];
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
delete this.index.entries[path];
|
|
441
|
+
this._unlinkFromParent(path);
|
|
442
|
+
this._save();
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
renameSync(oldPath, newPath) {
|
|
446
|
+
this._assertReady();
|
|
447
|
+
oldPath = normalizePath(oldPath);
|
|
448
|
+
newPath = normalizePath(newPath);
|
|
449
|
+
const entry = this.index.entries[oldPath];
|
|
450
|
+
if (!entry) throw this._enoent('ENOENT', oldPath);
|
|
451
|
+
|
|
452
|
+
const newParent = dirname(newPath);
|
|
453
|
+
const pEntry = this.index.entries[newParent];
|
|
454
|
+
if (!pEntry || pEntry.type !== 'dir') throw this._enoent('ENOENT', newParent);
|
|
455
|
+
|
|
456
|
+
if (this.index.entries[newPath]) {
|
|
457
|
+
if (this.index.entries[newPath].type === 'dir') {
|
|
458
|
+
const err = new Error(`EISDIR: illegal operation on a directory, ${newPath}`); err.code = 'EISDIR'; throw err;
|
|
459
|
+
}
|
|
460
|
+
delete this.index.entries[newPath];
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
this.index.entries[newPath] = entry;
|
|
464
|
+
delete this.index.entries[oldPath];
|
|
465
|
+
|
|
466
|
+
this._unlinkFromParent(oldPath);
|
|
467
|
+
this._linkIntoParent(newPath);
|
|
468
|
+
|
|
469
|
+
if (entry.type === 'dir') {
|
|
470
|
+
const toFix = [];
|
|
471
|
+
for (const key of Object.keys(this.index.entries)) {
|
|
472
|
+
if (key !== oldPath && key.startsWith(oldPath + '/')) toFix.push(key);
|
|
473
|
+
}
|
|
474
|
+
for (const oldChild of toFix) {
|
|
475
|
+
const rel = oldChild.slice(oldPath.length);
|
|
476
|
+
const target = normalizePath(newPath + rel);
|
|
477
|
+
this.index.entries[target] = this.index.entries[oldChild];
|
|
478
|
+
delete this.index.entries[oldChild];
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
entry.mtimeMs = nowMs();
|
|
483
|
+
this._save();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
statSync(path) {
|
|
487
|
+
this._assertReady();
|
|
488
|
+
path = normalizePath(path);
|
|
489
|
+
const entry = this.index.entries[path];
|
|
490
|
+
if (!entry) throw this._enoent('ENOENT', path);
|
|
491
|
+
return new Stats(entry);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
existsSync(path) {
|
|
495
|
+
this._assertReady();
|
|
496
|
+
path = normalizePath(path);
|
|
497
|
+
return !!this.index.entries[path];
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
_cbify(fn, ...args) {
|
|
501
|
+
const maybeCb = args[args.length - 1];
|
|
502
|
+
const hasCb = typeof maybeCb === 'function';
|
|
503
|
+
const core = () => {
|
|
504
|
+
try {
|
|
505
|
+
const res = fn.apply(this, hasCb ? args.slice(0, -1) : args);
|
|
506
|
+
if (hasCb) setTimeout(() => maybeCb(null, res), 0);
|
|
507
|
+
else return res;
|
|
508
|
+
} catch (err) {
|
|
509
|
+
if (hasCb) setTimeout(() => maybeCb(err), 0);
|
|
510
|
+
else throw err;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
return core();
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Async (Node-style callbacks)
|
|
517
|
+
readFile(...args) { return this._cbify(this.readFileSync, ...args); }
|
|
518
|
+
writeFile(...args) { return this._cbify(this.writeFileSync, ...args); }
|
|
519
|
+
appendFile(...args) { return this._cbify(this.appendFileSync, ...args); }
|
|
520
|
+
mkdir(...args) { return this._cbify(this.mkdirSync, ...args); }
|
|
521
|
+
readdir(...args) { return this._cbify(this.readdirSync, ...args); }
|
|
522
|
+
unlink(...args) { return this._cbify(this.unlinkSync, ...args); }
|
|
523
|
+
rmdir(...args) { return this._cbify(this.rmdirSync, ...args); }
|
|
524
|
+
rename(...args) { return this._cbify(this.renameSync, ...args); }
|
|
525
|
+
stat(...args) { return this._cbify(this.statSync, ...args); }
|
|
526
|
+
|
|
527
|
+
get promises() {
|
|
528
|
+
const wrap = (syncFn) => (...args) => {
|
|
529
|
+
return new Promise((resolve, reject) => {
|
|
530
|
+
try { const res = syncFn.apply(this, args); resolve(res); }
|
|
531
|
+
catch (e) { reject(e); }
|
|
532
|
+
});
|
|
533
|
+
};
|
|
534
|
+
return {
|
|
535
|
+
readFile: wrap(this.readFileSync),
|
|
536
|
+
writeFile: wrap(this.writeFileSync),
|
|
537
|
+
appendFile: wrap(this.appendFileSync),
|
|
538
|
+
mkdir: wrap(this.mkdirSync),
|
|
539
|
+
readdir: wrap(this.readdirSync),
|
|
540
|
+
unlink: wrap(this.unlinkSync),
|
|
541
|
+
rmdir: wrap(this.rmdirSync),
|
|
542
|
+
rename: wrap(this.renameSync),
|
|
543
|
+
stat: wrap(this.statSync),
|
|
544
|
+
rm: wrap(this.rmSync),
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
rmSync(path, options = {}) {
|
|
549
|
+
this._assertReady();
|
|
550
|
+
path = normalizePath(path);
|
|
551
|
+
const { force = false, recursive = false } = (typeof options === 'object' && options) || {};
|
|
552
|
+
if (path === '/') { const err = new Error(`EPERM: operation not permitted, rm '${path}'`); err.code = 'EPERM'; throw err; }
|
|
553
|
+
const entry = this.index.entries[path];
|
|
554
|
+
if (!entry) { if (force) return; throw this._enoent('ENOENT', path); }
|
|
555
|
+
if (entry.type === 'file') { this.unlinkSync(path); return; }
|
|
556
|
+
if (recursive) this.rmdirSync(path, { recursive: true });
|
|
557
|
+
else this.rmdirSync(path);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const browserFs = (typeof window !== 'undefined') ? new IndexedDbFS() : null;
|
|
562
|
+
|
|
563
|
+
// -------------------- Public Export --------------------
|
|
564
|
+
|
|
565
|
+
const universalFs = {
|
|
566
|
+
ready: () => (browserFs ? browserFs.ready() : Promise.resolve()),
|
|
567
|
+
// Async callback-style
|
|
568
|
+
readFile: async (...args) => { if (isNode) { await loadNodeFsIfNeeded(); return nodeFs.readFile(...args); } await browserFs.ready(); return browserFs.readFile(...args); },
|
|
569
|
+
writeFile: async (...args) => { if (isNode) { await loadNodeFsIfNeeded(); return nodeFs.writeFile(...args); } await browserFs.ready(); return browserFs.writeFile(...args); },
|
|
570
|
+
appendFile: async (...args) => { if (isNode) { await loadNodeFsIfNeeded(); return nodeFs.appendFile(...args); } await browserFs.ready(); return browserFs.appendFile(...args); },
|
|
571
|
+
mkdir: async (...args) => { if (isNode) { await loadNodeFsIfNeeded(); return nodeFs.mkdir(...args); } await browserFs.ready(); return browserFs.mkdir(...args); },
|
|
572
|
+
readdir: async (...args) => { if (isNode) { await loadNodeFsIfNeeded(); return nodeFs.readdir(...args); } await browserFs.ready(); return browserFs.readdir(...args); },
|
|
573
|
+
unlink: async (...args) => { if (isNode) { await loadNodeFsIfNeeded(); return nodeFs.unlink(...args); } await browserFs.ready(); return browserFs.unlink(...args); },
|
|
574
|
+
rmdir: async (...args) => { if (isNode) { await loadNodeFsIfNeeded(); return nodeFs.rmdir(...args); } await browserFs.ready(); return browserFs.rmdir(...args); },
|
|
575
|
+
rename: async (...args) => { if (isNode) { await loadNodeFsIfNeeded(); return nodeFs.rename(...args); } await browserFs.ready(); return browserFs.rename(...args); },
|
|
576
|
+
stat: async (...args) => { if (isNode) { await loadNodeFsIfNeeded(); return nodeFs.stat(...args); } await browserFs.ready(); return browserFs.stat(...args); },
|
|
577
|
+
rm: async (...args) => { if (isNode) { await loadNodeFsIfNeeded(); return nodeFs.rm(...args); } await browserFs.ready(); return browserFs.rm(...args); },
|
|
578
|
+
|
|
579
|
+
// Sync methods - supported in Node ESM via createRequire, and in CJS via require.
|
|
580
|
+
readFileSync: (...args) => { if (isNode) return requireLikeFsSync().readFileSync(...args); return browserFs.readFileSync(...args); },
|
|
581
|
+
writeFileSync: (...args) => { if (isNode) return requireLikeFsSync().writeFileSync(...args); return browserFs.writeFileSync(...args); },
|
|
582
|
+
appendFileSync: (...args) => { if (isNode) return requireLikeFsSync().appendFileSync(...args); return browserFs.appendFileSync(...args); },
|
|
583
|
+
mkdirSync: (...args) => { if (isNode) return requireLikeFsSync().mkdirSync(...args); return browserFs.mkdirSync(...args); },
|
|
584
|
+
readdirSync: (...args) => { if (isNode) return requireLikeFsSync().readdirSync(...args); return browserFs.readdirSync(...args); },
|
|
585
|
+
unlinkSync: (...args) => { if (isNode) return requireLikeFsSync().unlinkSync(...args); return browserFs.unlinkSync(...args); },
|
|
586
|
+
rmdirSync: (...args) => { if (isNode) return requireLikeFsSync().rmdirSync(...args); return browserFs.rmdirSync(...args); },
|
|
587
|
+
renameSync: (...args) => { if (isNode) return requireLikeFsSync().renameSync(...args); return browserFs.renameSync(...args); },
|
|
588
|
+
statSync: (...args) => { if (isNode) return requireLikeFsSync().statSync(...args); return browserFs.statSync(...args); },
|
|
589
|
+
existsSync: (...args) => { if (isNode) return requireLikeFsSync().existsSync(...args); return browserFs.existsSync(...args); },
|
|
590
|
+
rmSync: (...args) => { if (isNode) return requireLikeFsSync().rmSync(...args); return browserFs.rmSync(...args); },
|
|
591
|
+
|
|
592
|
+
// promises API delegated lazily
|
|
593
|
+
promises: new Proxy({}, {
|
|
594
|
+
get: (_, prop) => {
|
|
595
|
+
if (isNode) {
|
|
596
|
+
return async (...args) => {
|
|
597
|
+
await loadNodeFsIfNeeded();
|
|
598
|
+
const fn = nodeFsPromises[prop];
|
|
599
|
+
if (typeof fn !== 'function') throw new Error(`fs.promises.${String(prop)} is not available`);
|
|
600
|
+
return fn(...args);
|
|
601
|
+
};
|
|
602
|
+
} else {
|
|
603
|
+
return async (...args) => {
|
|
604
|
+
await browserFs.ready();
|
|
605
|
+
const prom = browserFs.promises;
|
|
606
|
+
const fn = prom[prop];
|
|
607
|
+
if (typeof fn !== 'function') throw new Error(`fs.promises.${String(prop)} is not implemented in browser VFS`);
|
|
608
|
+
return fn(...args);
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}),
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
// Helper: obtain sync fs in Node from either CJS require or ESM createRequire.
|
|
616
|
+
function requireLikeFsSync() {
|
|
617
|
+
if (!isNode) throw new Error("fs sync shim unavailable in this environment");
|
|
618
|
+
// if preload somehow hasn't started, start it now
|
|
619
|
+
if (!_preloadFsStarted) _kickoffNodeFsPreload();
|
|
620
|
+
if (!nodeFsSync) {
|
|
621
|
+
throw new Error("Synchronous fs not ready yet in Node ESM. Use fs.promises early in startup or run under CommonJS.");
|
|
622
|
+
}
|
|
623
|
+
return nodeFsSync;
|
|
624
|
+
}
|
|
625
|
+
export const fs = universalFs;
|