@superblocksteam/cli 2.0.6-next.16 → 2.0.6-next.18
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/README.md +1 -1
- package/dist/index.js +335 -149
- package/oclif.manifest.json +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ $ npm install -g @superblocksteam/cli
|
|
|
14
14
|
$ superblocks COMMAND
|
|
15
15
|
running command...
|
|
16
16
|
$ superblocks (--version)
|
|
17
|
-
@superblocksteam/cli/2.0.6-next.
|
|
17
|
+
@superblocksteam/cli/2.0.6-next.18 linux-x64 node-v20.19.0
|
|
18
18
|
$ superblocks --help [COMMAND]
|
|
19
19
|
USAGE
|
|
20
20
|
$ superblocks COMMAND
|
package/dist/index.js
CHANGED
|
@@ -413073,7 +413073,7 @@ init_cjs_shims();
|
|
|
413073
413073
|
var templates = {
|
|
413074
413074
|
"api-builder-to-yaml": {
|
|
413075
413075
|
"bootstrap.mjs": 'import { register } from "node:module";\nimport { pathToFileURL } from "node:url";\n\nregister("./shim-loader.mjs", pathToFileURL("./"));\n',
|
|
413076
|
-
"package.json": '{\n "type": "module",\n "dependencies": {\n "acorn": "^8.14.0",\n "prettier": "^3.5.3",\n "yaml": "^2.7.1"\n },\n "devDependencies": {\n "@types/node": "^22.15.18",\n "@typescript-eslint/parser": "^8.17.0",\n "eslint": "^9.26.0",\n "typescript": "^5.8.3",\n "vitest": "^3.0
|
|
413076
|
+
"package.json": '{\n "type": "module",\n "dependencies": {\n "acorn": "^8.14.0",\n "prettier": "^3.5.3",\n "yaml": "^2.7.1"\n },\n "devDependencies": {\n "@types/node": "^22.15.18",\n "@typescript-eslint/parser": "^8.17.0",\n "eslint": "^9.26.0",\n "typescript": "^5.8.3",\n "vitest": "^3.2.0"\n }\n}\n',
|
|
413077
413077
|
"shim-loader.mjs": 'import path from "path";\nimport { pathToFileURL } from "url";\n\nconst overrideMap = {\n "@superblocksteam/library": "./dist/superblocks-library-shim/index.js",\n};\n\nexport async function resolve(specifier, context, nextResolve) {\n if (overrideMap[specifier]) {\n const fullPath = path.resolve(overrideMap[specifier]);\n return {\n shortCircuit: true,\n url: pathToFileURL(fullPath).href,\n };\n }\n\n return nextResolve(specifier, context);\n}\n',
|
|
413078
413078
|
"src/do-eval-to-sdk.ts": 'import fs from "fs";\nimport path from "path";\nimport { fileURLToPath } from "url";\n\n// finds all JavaScript files in the `dist/apis-to-transform` directory,\n// imports each module, converts its JSON representation to SDK,\n// and outputs a JSON map with filenames as keys and SDK content as values.\n\nasync function main() {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n\n const apisDir = path.resolve(__dirname, "./to-sdk");\n\n const files = fs.readdirSync(apisDir);\n\n const jsFiles = files.filter(\n (file) => file.endsWith(".js") && file !== "__template__.js",\n );\n\n const results: Record<string, string> = {};\n\n for (const jsFile of jsFiles) {\n const filePath = path.join(apisDir, jsFile);\n const absolutePath = path.resolve(filePath);\n\n try {\n const fileUrl = new URL(`file://${absolutePath}`);\n const module = await import(fileUrl.href);\n\n const defaultExport = module.default;\n\n if (defaultExport && typeof defaultExport.toSDK === "function") {\n const sdkData = await defaultExport.toSDK();\n\n results[jsFile] = sdkData;\n } else {\n console.warn(`${jsFile}: Default export doesn\'t have a toSDK method`);\n }\n } catch (error) {\n console.error(`Error processing ${jsFile}:`, error);\n }\n }\n\n console.log(JSON.stringify(results, null, 2));\n}\n\nmain().catch((error) => {\n console.error("Error:", error);\n process.exit(1);\n});\n',
|
|
413079
413079
|
"src/do-eval-to-yaml.ts": 'import fs from "fs";\nimport path from "path";\nimport { fileURLToPath } from "url";\nimport yaml from "yaml";\n\n// finds all JavaScript files in the `dist/apis-to-transform` directory,\n// imports each module, converts its JSON representation to YAML,\n// and outputs a JSON map with filenames as keys and YAML content as values.\n\nasync function main() {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = path.dirname(__filename);\n\n const apisDir = path.resolve(__dirname, "./to-yaml");\n\n const files = fs.readdirSync(apisDir);\n\n const jsFiles = files.filter((file) => file.endsWith(".js"));\n\n const results: Record<string, string> = {};\n\n for (const jsFile of jsFiles) {\n const filePath = path.join(apisDir, jsFile);\n const absolutePath = path.resolve(filePath);\n\n try {\n const fileUrl = new URL(`file://${absolutePath}`);\n const module = await import(fileUrl.href);\n\n const defaultExport = module.default;\n\n if (defaultExport && typeof defaultExport.toJSON === "function") {\n const jsonData = await defaultExport.toJSON();\n\n const yamlContent = yaml.stringify(jsonData);\n\n results[jsFile] = yamlContent;\n } else {\n console.warn(`${jsFile}: Default export doesn\'t have a toJSON method`);\n }\n } catch (error) {\n console.error(`Error processing ${jsFile}:`, error);\n }\n }\n\n console.log(JSON.stringify(results, null, 2));\n}\n\nmain().catch((error) => {\n console.error("Error:", error);\n process.exit(1);\n});\n',
|
|
@@ -413290,6 +413290,7 @@ var Paths;
|
|
|
413290
413290
|
})(Paths || (Paths = {}));
|
|
413291
413291
|
var LOG_SYSTEM_PROMPT = false;
|
|
413292
413292
|
var SAVE_LLM_GENERATION_STEPS = false;
|
|
413293
|
+
var SAVE_RAW_LLM_RESPONSE = false;
|
|
413293
413294
|
|
|
413294
413295
|
// ../../../vite-plugin-file-sync/dist/ai-service/integrations/from-prompt-context.js
|
|
413295
413296
|
init_cjs_shims();
|
|
@@ -417871,6 +417872,7 @@ Address the errors and return the fixed code.`;
|
|
|
417871
417872
|
switch (event.type) {
|
|
417872
417873
|
case USER_SENT_PROMPT: {
|
|
417873
417874
|
const { request, peer } = event;
|
|
417875
|
+
params.signals.emit("generationStarted");
|
|
417874
417876
|
const initialElementIds = clark.context.initialElementIds ?? Object.keys(params.sourceTrackerInterface.getElementToFilePathMap() ?? {});
|
|
417875
417877
|
clark.updateContext((context2) => ({
|
|
417876
417878
|
...context2,
|
|
@@ -424480,6 +424482,7 @@ var doLLMGenerating = (clark, { anthropicProvider, appShell, artifactProcessor,
|
|
|
424480
424482
|
let artifactChunk = null;
|
|
424481
424483
|
let stepId = null;
|
|
424482
424484
|
const flushedAfterArtifacts = [];
|
|
424485
|
+
let rawLLMResponse = "";
|
|
424483
424486
|
const resultBuffer = new BoltResultBuffer({
|
|
424484
424487
|
onTypeChange: async (type2) => {
|
|
424485
424488
|
switch (type2) {
|
|
@@ -424589,6 +424592,9 @@ var doLLMGenerating = (clark, { anthropicProvider, appShell, artifactProcessor,
|
|
|
424589
424592
|
for await (const chunk of fullStream) {
|
|
424590
424593
|
switch (chunk.type) {
|
|
424591
424594
|
case "text-delta":
|
|
424595
|
+
if (SAVE_LLM_GENERATION_STEPS) {
|
|
424596
|
+
rawLLMResponse += chunk.textDelta;
|
|
424597
|
+
}
|
|
424592
424598
|
resultBuffer.add(chunk.textDelta);
|
|
424593
424599
|
resultBuffer.maybeFlush();
|
|
424594
424600
|
break;
|
|
@@ -424659,6 +424665,19 @@ var doLLMGenerating = (clark, { anthropicProvider, appShell, artifactProcessor,
|
|
|
424659
424665
|
}
|
|
424660
424666
|
}
|
|
424661
424667
|
resultBuffer.maybeFlush(true);
|
|
424668
|
+
if (SAVE_RAW_LLM_RESPONSE && rawLLMResponse.length > 0) {
|
|
424669
|
+
try {
|
|
424670
|
+
logger3.debug(`[ai-service] Raw LLM response length: ${rawLLMResponse.length} characters`);
|
|
424671
|
+
void appShell.saveGeneratedArtifact({
|
|
424672
|
+
type: "file",
|
|
424673
|
+
filePath: "raw-llm-response.txt",
|
|
424674
|
+
content: rawLLMResponse
|
|
424675
|
+
}, stepId ?? "unknown-step-id", clark.context.runTimestamp ?? "unknown-run-timestamp");
|
|
424676
|
+
logger3.info(`[ai-service] Raw LLM response saved to debug folder`);
|
|
424677
|
+
} catch (error) {
|
|
424678
|
+
logger3.error("[ai-service] Failed to save raw LLM response:", getErrorMeta(error));
|
|
424679
|
+
}
|
|
424680
|
+
}
|
|
424662
424681
|
}
|
|
424663
424682
|
}
|
|
424664
424683
|
};
|
|
@@ -431924,6 +431943,9 @@ function getPageFolder(rootPath, pageName) {
|
|
|
431924
431943
|
function getPageFilePath(rootPath, pageName) {
|
|
431925
431944
|
return path21.join(getPageFolder(rootPath, pageName), "index.tsx");
|
|
431926
431945
|
}
|
|
431946
|
+
function isPageFilePath(filePath) {
|
|
431947
|
+
return /pages\/.*\/index\.tsx/.test(filePath);
|
|
431948
|
+
}
|
|
431927
431949
|
|
|
431928
431950
|
// ../../../vite-plugin-file-sync/dist/traverse.js
|
|
431929
431951
|
init_cjs_shims();
|
|
@@ -433797,6 +433819,87 @@ function addLegacyCustomComponentVariables(path55, tracker) {
|
|
|
433797
433819
|
});
|
|
433798
433820
|
}
|
|
433799
433821
|
|
|
433822
|
+
// ../../../vite-plugin-file-sync/dist/operations/operation-processor.js
|
|
433823
|
+
init_cjs_shims();
|
|
433824
|
+
var OperationProcessor = class {
|
|
433825
|
+
config;
|
|
433826
|
+
operationQueue;
|
|
433827
|
+
pendingOperations = [];
|
|
433828
|
+
batchTimer = null;
|
|
433829
|
+
_enabled = true;
|
|
433830
|
+
constructor(config2 = {}) {
|
|
433831
|
+
this.config = {
|
|
433832
|
+
batchWindowMs: 50,
|
|
433833
|
+
maxBatchSize: 100,
|
|
433834
|
+
...config2
|
|
433835
|
+
};
|
|
433836
|
+
this.operationQueue = new OperationQueue();
|
|
433837
|
+
}
|
|
433838
|
+
async addOperation(operation) {
|
|
433839
|
+
const timestamp2 = Date.now();
|
|
433840
|
+
const id2 = `operation-${timestamp2}-${Math.random()}`;
|
|
433841
|
+
const fullOperation = {
|
|
433842
|
+
...operation,
|
|
433843
|
+
id: id2,
|
|
433844
|
+
timestamp: timestamp2
|
|
433845
|
+
};
|
|
433846
|
+
if (!this._enabled || fullOperation.priority) {
|
|
433847
|
+
return this.processOperations([fullOperation]);
|
|
433848
|
+
}
|
|
433849
|
+
this.pendingOperations.push(fullOperation);
|
|
433850
|
+
if (this.pendingOperations.length >= this.config.maxBatchSize) {
|
|
433851
|
+
return this.flush();
|
|
433852
|
+
}
|
|
433853
|
+
this.resetBatchTimer();
|
|
433854
|
+
}
|
|
433855
|
+
async flush() {
|
|
433856
|
+
if (this.pendingOperations.length === 0) {
|
|
433857
|
+
return;
|
|
433858
|
+
}
|
|
433859
|
+
const operations = [...this.pendingOperations];
|
|
433860
|
+
this.pendingOperations = [];
|
|
433861
|
+
this.clearBatchTimer();
|
|
433862
|
+
return this.operationQueue.enqueue(() => this.processOperations(operations));
|
|
433863
|
+
}
|
|
433864
|
+
disable() {
|
|
433865
|
+
this.setEnabled(false);
|
|
433866
|
+
}
|
|
433867
|
+
enable() {
|
|
433868
|
+
this.setEnabled(true);
|
|
433869
|
+
}
|
|
433870
|
+
setEnabled(enabled) {
|
|
433871
|
+
const wasEnabled = this._enabled;
|
|
433872
|
+
this._enabled = enabled;
|
|
433873
|
+
if (!enabled && wasEnabled && this.pendingOperations.length > 0) {
|
|
433874
|
+
this.flush().catch((error) => {
|
|
433875
|
+
console.error("Error flushing operations on priority mode enable:", error);
|
|
433876
|
+
});
|
|
433877
|
+
}
|
|
433878
|
+
}
|
|
433879
|
+
async processOperations(operations) {
|
|
433880
|
+
if (operations.length === 0) {
|
|
433881
|
+
return;
|
|
433882
|
+
}
|
|
433883
|
+
for (const operation of operations) {
|
|
433884
|
+
await operation.execute();
|
|
433885
|
+
}
|
|
433886
|
+
}
|
|
433887
|
+
resetBatchTimer() {
|
|
433888
|
+
this.clearBatchTimer();
|
|
433889
|
+
this.batchTimer = setTimeout(() => {
|
|
433890
|
+
this.flush().catch((error) => {
|
|
433891
|
+
console.error("Error flushing operations on timer:", error);
|
|
433892
|
+
});
|
|
433893
|
+
}, this.config.batchWindowMs);
|
|
433894
|
+
}
|
|
433895
|
+
clearBatchTimer() {
|
|
433896
|
+
if (this.batchTimer) {
|
|
433897
|
+
clearTimeout(this.batchTimer);
|
|
433898
|
+
this.batchTimer = null;
|
|
433899
|
+
}
|
|
433900
|
+
}
|
|
433901
|
+
};
|
|
433902
|
+
|
|
433800
433903
|
// ../../../vite-plugin-file-sync/dist/parsing/bindings.js
|
|
433801
433904
|
init_cjs_shims();
|
|
433802
433905
|
var localBindingKinds = /* @__PURE__ */ new Set(["local", "param", "var", "const", "let"]);
|
|
@@ -435896,6 +435999,7 @@ var SourceTracker = class {
|
|
|
435896
435999
|
const extension = path26.extname(fileName);
|
|
435897
436000
|
if (extension !== ".ts" && extension !== ".tsx")
|
|
435898
436001
|
return;
|
|
436002
|
+
const existingFile = fileName in this.fileToMeta;
|
|
435899
436003
|
try {
|
|
435900
436004
|
const astMap = this.parseJsxFromEntrypoint({
|
|
435901
436005
|
files: [{ fileName, code: fileContents }]
|
|
@@ -435918,7 +436022,19 @@ var SourceTracker = class {
|
|
|
435918
436022
|
delete this.elementToLocation[key2];
|
|
435919
436023
|
delete this.elementToFilePath[key2];
|
|
435920
436024
|
});
|
|
435921
|
-
|
|
436025
|
+
let changedFiles = [];
|
|
436026
|
+
if (existingFile) {
|
|
436027
|
+
changedFiles = this.sbScopeManager.resetScope(fileName);
|
|
436028
|
+
} else {
|
|
436029
|
+
this.sbScopeManager.parseScopes({
|
|
436030
|
+
[fileName]: {
|
|
436031
|
+
code: fileContents,
|
|
436032
|
+
ast,
|
|
436033
|
+
imports: extractImportsFromAst(ast)
|
|
436034
|
+
}
|
|
436035
|
+
});
|
|
436036
|
+
changedFiles.push(fileName);
|
|
436037
|
+
}
|
|
435922
436038
|
if (!fileName.endsWith(SCOPE_FILE)) {
|
|
435923
436039
|
const { idMap } = supplementElementIds({
|
|
435924
436040
|
fileName,
|
|
@@ -436044,7 +436160,7 @@ var SourceTracker = class {
|
|
|
436044
436160
|
});
|
|
436045
436161
|
return scopeDefinition.id;
|
|
436046
436162
|
};
|
|
436047
|
-
deleteApi =
|
|
436163
|
+
deleteApi = ({ pageName, apiName }) => {
|
|
436048
436164
|
const { scopeDefinition, filePath: scopeFilePath } = this.sbScopeManager.getScopeDefinitionAndFilePathByNameOrId({
|
|
436049
436165
|
identifier: pageName,
|
|
436050
436166
|
type: "name"
|
|
@@ -436076,24 +436192,24 @@ var SourceTracker = class {
|
|
|
436076
436192
|
});
|
|
436077
436193
|
return scopeDefinition.id;
|
|
436078
436194
|
};
|
|
436079
|
-
addEntity =
|
|
436195
|
+
addEntity = ({ scopeId, entity }) => {
|
|
436080
436196
|
const changedFiles = this.sbScopeManager.addScopeEntity(scopeId, entity);
|
|
436081
436197
|
changedFiles.forEach((file) => {
|
|
436082
436198
|
this.changedFiles.add(file);
|
|
436083
436199
|
});
|
|
436084
436200
|
};
|
|
436085
|
-
updateEntity =
|
|
436201
|
+
updateEntity = (payload) => {
|
|
436086
436202
|
const changedFile = this.sbScopeManager.updateScopeEntity(payload);
|
|
436087
436203
|
this.changedFiles.add(changedFile);
|
|
436088
436204
|
};
|
|
436089
|
-
deleteEntity =
|
|
436205
|
+
deleteEntity = ({ entityId }) => {
|
|
436090
436206
|
const { changedFiles, deletedEntitiesNames } = this.sbScopeManager.deleteScopeEntity(entityId);
|
|
436091
436207
|
changedFiles.forEach((file) => {
|
|
436092
436208
|
this.changedFiles.add(file);
|
|
436093
436209
|
});
|
|
436094
436210
|
return deletedEntitiesNames[0];
|
|
436095
436211
|
};
|
|
436096
|
-
renameEntity =
|
|
436212
|
+
renameEntity = ({ entityId, oldName, newName, scopeName }) => {
|
|
436097
436213
|
const { changedFiles, scopeFile } = this.sbScopeManager.renameScopeEntity({
|
|
436098
436214
|
entityId,
|
|
436099
436215
|
newName,
|
|
@@ -436124,7 +436240,7 @@ var SourceTracker = class {
|
|
|
436124
436240
|
}
|
|
436125
436241
|
};
|
|
436126
436242
|
// THEMING
|
|
436127
|
-
updateTheme =
|
|
436243
|
+
updateTheme = ({ themeFilePath, theme }) => {
|
|
436128
436244
|
const ast = this.getCurrentFiles()[themeFilePath]?.ast;
|
|
436129
436245
|
if (!ast) {
|
|
436130
436246
|
throw new Error("File not found in source tracker " + path26);
|
|
@@ -436137,7 +436253,7 @@ var SourceTracker = class {
|
|
|
436137
436253
|
throw e;
|
|
436138
436254
|
}
|
|
436139
436255
|
};
|
|
436140
|
-
addElement =
|
|
436256
|
+
addElement = ({ parentElement, tagName, properties, children, id: id2, scopeName }) => {
|
|
436141
436257
|
const logger3 = getLogger();
|
|
436142
436258
|
const parentOpeningElementPath = this.getElementToLocation(parentElement.source.id);
|
|
436143
436259
|
const parentFilepath = this.getElementToFilePath(parentElement.source.id);
|
|
@@ -436281,7 +436397,7 @@ var SourceTracker = class {
|
|
|
436281
436397
|
throw new Error("Error updating source tracker during add" + e.message);
|
|
436282
436398
|
}
|
|
436283
436399
|
};
|
|
436284
|
-
deleteElement =
|
|
436400
|
+
deleteElement = ({ source: source2, scopeName }) => {
|
|
436285
436401
|
const logger3 = getLogger();
|
|
436286
436402
|
let openingTag = this.getElementToLocation(source2.id);
|
|
436287
436403
|
if (!openingTag) {
|
|
@@ -436440,13 +436556,13 @@ var SourceTracker = class {
|
|
|
436440
436556
|
element.node.closingElement = import_types24.default.jsxClosingElement(import_types24.default.jsxIdentifier(getTagName(openingElement.node.name) ?? ""));
|
|
436441
436557
|
}
|
|
436442
436558
|
};
|
|
436443
|
-
setProperty =
|
|
436444
|
-
|
|
436559
|
+
setProperty = ({ source: source2, property, info }) => {
|
|
436560
|
+
this.setProperties({
|
|
436445
436561
|
source: source2,
|
|
436446
436562
|
changes: { [property]: info }
|
|
436447
436563
|
});
|
|
436448
436564
|
};
|
|
436449
|
-
setProperties =
|
|
436565
|
+
setProperties = ({ source: source2, changes }) => {
|
|
436450
436566
|
const logger3 = getLogger();
|
|
436451
436567
|
if (Object.keys(changes).length === 0)
|
|
436452
436568
|
return;
|
|
@@ -436672,12 +436788,13 @@ var SUPPORTED_FILETYPES = [
|
|
|
436672
436788
|
}
|
|
436673
436789
|
];
|
|
436674
436790
|
var APP_THEME_FILE_NAME = "appTheme.ts";
|
|
436675
|
-
var
|
|
436791
|
+
var FileSystemManager = class extends import_shared32.TracedEventEmitter {
|
|
436676
436792
|
rootDir;
|
|
436677
436793
|
tsFiles = {};
|
|
436678
436794
|
apiFiles = {};
|
|
436679
436795
|
sourceTracker;
|
|
436680
436796
|
fsOperationQueue;
|
|
436797
|
+
operationProcessor;
|
|
436681
436798
|
generationNumberSequence;
|
|
436682
436799
|
routes = {};
|
|
436683
436800
|
watcher;
|
|
@@ -436690,6 +436807,11 @@ var FileSyncManager = class extends import_shared32.TracedEventEmitter {
|
|
|
436690
436807
|
this.fsOperationQueue = fsOperationQueue;
|
|
436691
436808
|
this.generationNumberSequence = generationNumberSequence;
|
|
436692
436809
|
this._tracer = tracer2;
|
|
436810
|
+
this.operationProcessor = new OperationProcessor({
|
|
436811
|
+
batchWindowMs: 50,
|
|
436812
|
+
maxBatchSize: 100
|
|
436813
|
+
});
|
|
436814
|
+
this.operationProcessor.disable();
|
|
436693
436815
|
applyErrorHandling(this, {
|
|
436694
436816
|
watch: { operation: "editing from code" },
|
|
436695
436817
|
handleCreatePage: { operation: "creating a page" },
|
|
@@ -436836,132 +436958,193 @@ var FileSyncManager = class extends import_shared32.TracedEventEmitter {
|
|
|
436836
436958
|
this.updateApi(content2, path55);
|
|
436837
436959
|
}
|
|
436838
436960
|
});
|
|
436839
|
-
|
|
436840
|
-
|
|
436841
|
-
|
|
436842
|
-
|
|
436843
|
-
|
|
436844
|
-
|
|
436845
|
-
|
|
436846
|
-
|
|
436847
|
-
|
|
436848
|
-
|
|
436849
|
-
|
|
436850
|
-
|
|
436851
|
-
|
|
436852
|
-
|
|
436853
|
-
|
|
436854
|
-
|
|
436855
|
-
|
|
436856
|
-
|
|
436857
|
-
|
|
436858
|
-
|
|
436859
|
-
|
|
436961
|
+
watcher.on("all", this.handleFileChange);
|
|
436962
|
+
}
|
|
436963
|
+
enableOperationsQueue() {
|
|
436964
|
+
this.operationProcessor.enable();
|
|
436965
|
+
}
|
|
436966
|
+
disableOperationsQueue() {
|
|
436967
|
+
this.operationProcessor.disable();
|
|
436968
|
+
}
|
|
436969
|
+
async flushOperations() {
|
|
436970
|
+
await this.operationProcessor.flush();
|
|
436971
|
+
}
|
|
436972
|
+
handleFileChange = async (event, filePath) => {
|
|
436973
|
+
const logger3 = getLogger();
|
|
436974
|
+
logger3.info(`File changed: ${filePath}, event: ${event}`);
|
|
436975
|
+
const rootPath = this.rootDir;
|
|
436976
|
+
if (!rootPath) {
|
|
436977
|
+
throw new Error("Root directory not set");
|
|
436978
|
+
}
|
|
436979
|
+
if (event === "addDir" || event === "unlinkDir") {
|
|
436980
|
+
return;
|
|
436981
|
+
}
|
|
436982
|
+
const routePath = path27.join(rootPath, ROUTES_FILE);
|
|
436983
|
+
const fileType = SUPPORTED_FILETYPES.find((f) => filePath.endsWith(f.extension));
|
|
436984
|
+
if (!fileType || !filePath.startsWith(rootPath)) {
|
|
436985
|
+
return;
|
|
436986
|
+
}
|
|
436987
|
+
switch (event) {
|
|
436988
|
+
case "add": {
|
|
436989
|
+
const data = await readFile6(filePath);
|
|
436990
|
+
if (typeof data !== "string")
|
|
436991
|
+
return;
|
|
436992
|
+
const isPage = isPageFilePath(filePath);
|
|
436993
|
+
if (isPage) {
|
|
436994
|
+
void this.operationProcessor.addOperation({
|
|
436995
|
+
metadata: {
|
|
436996
|
+
filePath
|
|
436997
|
+
},
|
|
436998
|
+
execute: async () => {
|
|
436999
|
+
const file = await readFile6(filePath);
|
|
437000
|
+
if (!file) {
|
|
437001
|
+
logger3.error(`Failed to read file: ${filePath}`);
|
|
437002
|
+
return;
|
|
437003
|
+
}
|
|
437004
|
+
if (!(filePath in this.tsFiles)) {
|
|
437005
|
+
this.tsFiles[filePath] = file;
|
|
437006
|
+
this.handleNonVisualChangeByDeletingIds(filePath, file);
|
|
437007
|
+
this.emit("addPage", filePath);
|
|
437008
|
+
}
|
|
436860
437009
|
}
|
|
436861
|
-
}
|
|
436862
|
-
|
|
436863
|
-
|
|
436864
|
-
|
|
436865
|
-
|
|
436866
|
-
|
|
436867
|
-
|
|
437010
|
+
});
|
|
437011
|
+
} else {
|
|
437012
|
+
void this.operationProcessor.addOperation({
|
|
437013
|
+
metadata: {
|
|
437014
|
+
filePath
|
|
437015
|
+
},
|
|
437016
|
+
execute: async () => {
|
|
437017
|
+
switch (fileType.type) {
|
|
437018
|
+
case "api":
|
|
437019
|
+
case "python-api-step":
|
|
437020
|
+
case "js-api-step": {
|
|
437021
|
+
await this.processApiUpdates(filePath, fileType);
|
|
437022
|
+
break;
|
|
437023
|
+
}
|
|
437024
|
+
}
|
|
436868
437025
|
}
|
|
436869
|
-
}
|
|
436870
|
-
break;
|
|
437026
|
+
});
|
|
436871
437027
|
}
|
|
436872
|
-
|
|
436873
|
-
|
|
436874
|
-
|
|
436875
|
-
|
|
436876
|
-
|
|
436877
|
-
|
|
436878
|
-
|
|
436879
|
-
|
|
436880
|
-
|
|
436881
|
-
|
|
436882
|
-
|
|
436883
|
-
|
|
436884
|
-
|
|
436885
|
-
|
|
436886
|
-
|
|
436887
|
-
const data = await readFile6(filePath);
|
|
436888
|
-
if (typeof data !== "string")
|
|
436889
|
-
return;
|
|
436890
|
-
switch (fileType.type) {
|
|
436891
|
-
case "tsx":
|
|
436892
|
-
case "scope":
|
|
436893
|
-
{
|
|
436894
|
-
if (!(filePath in this.tsFiles && this.tsFiles[filePath] === data)) {
|
|
436895
|
-
logger3.info(`File changed: ${filePath} updating AST tracker`);
|
|
436896
|
-
this.tsFiles[filePath] = data;
|
|
436897
|
-
this.handleNonVisualChangeByDeletingIds(filePath, data);
|
|
436898
|
-
this.emit("fileChanged", filePath, data, true);
|
|
436899
|
-
} else {
|
|
436900
|
-
logger3.info(`File unchanged from last tracked state: ${filePath}`);
|
|
436901
|
-
this.emit("fileChanged", filePath, data, false);
|
|
437028
|
+
break;
|
|
437029
|
+
}
|
|
437030
|
+
case "change": {
|
|
437031
|
+
if (filePath === routePath) {
|
|
437032
|
+
void this.operationProcessor.addOperation({
|
|
437033
|
+
metadata: {
|
|
437034
|
+
filePath
|
|
437035
|
+
},
|
|
437036
|
+
priority: true,
|
|
437037
|
+
execute: async () => {
|
|
437038
|
+
try {
|
|
437039
|
+
const data = JSON.parse(await readFile6(filePath) ?? "{}");
|
|
437040
|
+
if (!isEqual_default(this.routes, data)) {
|
|
437041
|
+
this.routes = data;
|
|
437042
|
+
this.emit("routesChanged", this.routes);
|
|
436902
437043
|
}
|
|
437044
|
+
} catch (e) {
|
|
437045
|
+
logger3.error("Error parsing routes file", getErrorMeta(e));
|
|
436903
437046
|
}
|
|
436904
|
-
|
|
436905
|
-
|
|
436906
|
-
|
|
436907
|
-
|
|
436908
|
-
|
|
436909
|
-
|
|
437047
|
+
}
|
|
437048
|
+
});
|
|
437049
|
+
return;
|
|
437050
|
+
} else {
|
|
437051
|
+
void this.operationProcessor.addOperation({
|
|
437052
|
+
metadata: {
|
|
437053
|
+
filePath
|
|
437054
|
+
},
|
|
437055
|
+
execute: async () => {
|
|
437056
|
+
const fileType2 = SUPPORTED_FILETYPES.find((f) => filePath.endsWith(f.extension));
|
|
437057
|
+
if (!fileType2 || !filePath.startsWith(rootPath)) {
|
|
437058
|
+
return;
|
|
436910
437059
|
}
|
|
436911
|
-
|
|
436912
|
-
|
|
436913
|
-
|
|
436914
|
-
|
|
436915
|
-
|
|
436916
|
-
|
|
436917
|
-
|
|
436918
|
-
|
|
436919
|
-
|
|
436920
|
-
|
|
436921
|
-
|
|
436922
|
-
|
|
436923
|
-
|
|
436924
|
-
|
|
436925
|
-
|
|
436926
|
-
|
|
436927
|
-
|
|
436928
|
-
|
|
436929
|
-
|
|
436930
|
-
|
|
436931
|
-
|
|
436932
|
-
|
|
436933
|
-
|
|
437060
|
+
const data = await readFile6(filePath);
|
|
437061
|
+
if (typeof data !== "string")
|
|
437062
|
+
return;
|
|
437063
|
+
switch (fileType2.type) {
|
|
437064
|
+
case "tsx":
|
|
437065
|
+
case "scope":
|
|
437066
|
+
{
|
|
437067
|
+
if (!(filePath in this.tsFiles && this.tsFiles[filePath] === data)) {
|
|
437068
|
+
logger3.info(`File changed: ${filePath} updating AST tracker`);
|
|
437069
|
+
this.tsFiles[filePath] = data;
|
|
437070
|
+
this.handleNonVisualChangeByDeletingIds(filePath, data);
|
|
437071
|
+
this.emit("fileChanged", filePath, data, true);
|
|
437072
|
+
} else {
|
|
437073
|
+
logger3.info(`File unchanged from last tracked state: ${filePath}`);
|
|
437074
|
+
this.emit("fileChanged", filePath, data, false);
|
|
437075
|
+
}
|
|
437076
|
+
}
|
|
437077
|
+
break;
|
|
437078
|
+
case "api":
|
|
437079
|
+
case "python-api-step":
|
|
437080
|
+
case "js-api-step":
|
|
437081
|
+
{
|
|
437082
|
+
await this.processApiUpdates(filePath, fileType2);
|
|
437083
|
+
}
|
|
437084
|
+
break;
|
|
436934
437085
|
}
|
|
436935
|
-
}
|
|
436936
|
-
}
|
|
436937
|
-
if (filePath in this.tsFiles) {
|
|
436938
|
-
delete this.tsFiles[filePath];
|
|
436939
|
-
this.sourceTracker?.removeFile(filePath);
|
|
436940
|
-
this.emit("deletePage", filePath);
|
|
436941
|
-
}
|
|
436942
|
-
break;
|
|
437086
|
+
}
|
|
437087
|
+
});
|
|
436943
437088
|
}
|
|
437089
|
+
break;
|
|
436944
437090
|
}
|
|
436945
|
-
|
|
436946
|
-
|
|
436947
|
-
|
|
436948
|
-
|
|
436949
|
-
|
|
436950
|
-
|
|
436951
|
-
|
|
436952
|
-
|
|
437091
|
+
case "unlink": {
|
|
437092
|
+
if (filePath in this.tsFiles) {
|
|
437093
|
+
void this.operationProcessor.addOperation({
|
|
437094
|
+
metadata: {
|
|
437095
|
+
filePath
|
|
437096
|
+
},
|
|
437097
|
+
execute: async () => {
|
|
437098
|
+
await this.deleteTsFile(filePath);
|
|
437099
|
+
}
|
|
436953
437100
|
});
|
|
436954
|
-
|
|
436955
|
-
|
|
436956
|
-
|
|
436957
|
-
|
|
437101
|
+
} else if (filePath in this.apiFiles) {
|
|
437102
|
+
void this.operationProcessor.addOperation({
|
|
437103
|
+
metadata: {
|
|
437104
|
+
filePath
|
|
437105
|
+
},
|
|
437106
|
+
execute: async () => {
|
|
437107
|
+
await this.deleteApiFile(filePath);
|
|
437108
|
+
}
|
|
436958
437109
|
});
|
|
437110
|
+
}
|
|
437111
|
+
break;
|
|
436959
437112
|
}
|
|
436960
|
-
}
|
|
436961
|
-
}
|
|
437113
|
+
}
|
|
437114
|
+
};
|
|
436962
437115
|
getApiFiles() {
|
|
436963
437116
|
return this.formatApisToClientApis(this.apiFiles);
|
|
436964
437117
|
}
|
|
437118
|
+
async deleteTsFile(filePath) {
|
|
437119
|
+
delete this.tsFiles[filePath];
|
|
437120
|
+
this.sourceTracker?.removeFile(filePath);
|
|
437121
|
+
if (isPageFilePath(filePath)) {
|
|
437122
|
+
this.emit("deletePage", filePath);
|
|
437123
|
+
}
|
|
437124
|
+
}
|
|
437125
|
+
async deleteApiFile(filePath) {
|
|
437126
|
+
const api = this.apiFiles[filePath];
|
|
437127
|
+
if (!api) {
|
|
437128
|
+
return;
|
|
437129
|
+
}
|
|
437130
|
+
delete this.apiFiles[filePath];
|
|
437131
|
+
this.sourceTracker?.deleteApi({
|
|
437132
|
+
pageName: api.pageName,
|
|
437133
|
+
apiName: api.apiPb.metadata.name
|
|
437134
|
+
});
|
|
437135
|
+
const changes = await this.sourceTracker?.getAndFlushChanges() ?? [];
|
|
437136
|
+
await this.writeChanges(changes);
|
|
437137
|
+
const scopeId = this.sourceTracker?.getScopeDefinitionForPage(api.pageName)?.id;
|
|
437138
|
+
this.emit("apiManualDelete", {
|
|
437139
|
+
api: {
|
|
437140
|
+
id: getClientApiId(api.apiPb.metadata.name, api.pageName),
|
|
437141
|
+
apiName: api.apiPb.metadata.name,
|
|
437142
|
+
// TODO(saksham): get pagename more defensively
|
|
437143
|
+
pageName: getPageName2(filePath),
|
|
437144
|
+
scopeId
|
|
437145
|
+
}
|
|
437146
|
+
});
|
|
437147
|
+
}
|
|
436965
437148
|
getTsFilePaths() {
|
|
436966
437149
|
return Object.keys(this.tsFiles);
|
|
436967
437150
|
}
|
|
@@ -437169,11 +437352,11 @@ export default registerPage(Page, { name: "${name18}" });
|
|
|
437169
437352
|
handleReparent = async (payload, writeFile9 = true) => {
|
|
437170
437353
|
const { from, to, changedProps, transaction } = payload;
|
|
437171
437354
|
this.trackTransaction(transaction?.id);
|
|
437172
|
-
|
|
437355
|
+
this.sourceTracker?.setProperties({
|
|
437173
437356
|
source: from.source,
|
|
437174
437357
|
changes: changedProps ?? {}
|
|
437175
437358
|
});
|
|
437176
|
-
|
|
437359
|
+
this.sourceTracker?.moveElement({
|
|
437177
437360
|
from,
|
|
437178
437361
|
to
|
|
437179
437362
|
});
|
|
@@ -437187,7 +437370,7 @@ export default registerPage(Page, { name: "${name18}" });
|
|
|
437187
437370
|
};
|
|
437188
437371
|
handleCreateComponent = async (payload, writeFile9 = true) => {
|
|
437189
437372
|
this.trackTransaction(payload.transaction?.id);
|
|
437190
|
-
const sourceId =
|
|
437373
|
+
const sourceId = this.sourceTracker?.addElement(payload);
|
|
437191
437374
|
if (writeFile9) {
|
|
437192
437375
|
const changes = await this.sourceTracker?.getAndFlushChanges() ?? [];
|
|
437193
437376
|
await this.writeChanges(changes ?? [], (fileName) => {
|
|
@@ -437201,7 +437384,7 @@ export default registerPage(Page, { name: "${name18}" });
|
|
|
437201
437384
|
const { elements, transaction } = payload;
|
|
437202
437385
|
this.trackTransaction(transaction?.id);
|
|
437203
437386
|
for (const element of elements) {
|
|
437204
|
-
|
|
437387
|
+
this.sourceTracker?.deleteElement({
|
|
437205
437388
|
source: element.source,
|
|
437206
437389
|
scopeName: element.scopeName
|
|
437207
437390
|
});
|
|
@@ -437217,7 +437400,7 @@ export default registerPage(Page, { name: "${name18}" });
|
|
|
437217
437400
|
handleSetProperty = async (payload, writeFile9 = true) => {
|
|
437218
437401
|
const { element: { source: source2 }, property, value: value2, transaction } = payload;
|
|
437219
437402
|
this.trackTransaction(transaction?.id);
|
|
437220
|
-
|
|
437403
|
+
this.sourceTracker?.setProperty({
|
|
437221
437404
|
source: source2,
|
|
437222
437405
|
property,
|
|
437223
437406
|
info: value2
|
|
@@ -437232,7 +437415,7 @@ export default registerPage(Page, { name: "${name18}" });
|
|
|
437232
437415
|
handleSetProperties = async (payload, writeFile9 = true) => {
|
|
437233
437416
|
const { element: { source: source2 }, properties, transaction } = payload;
|
|
437234
437417
|
this.trackTransaction(transaction?.id);
|
|
437235
|
-
|
|
437418
|
+
this.sourceTracker?.setProperties({
|
|
437236
437419
|
source: source2,
|
|
437237
437420
|
changes: properties
|
|
437238
437421
|
});
|
|
@@ -437362,7 +437545,7 @@ export default registerPage(Page, { name: "${name18}" });
|
|
|
437362
437545
|
await fs12.rmdir(apiDir, { recursive: true });
|
|
437363
437546
|
}
|
|
437364
437547
|
delete this.apiFiles[apiFilePath];
|
|
437365
|
-
const scopeId =
|
|
437548
|
+
const scopeId = this.sourceTracker.deleteApi({
|
|
437366
437549
|
pageName,
|
|
437367
437550
|
apiName
|
|
437368
437551
|
});
|
|
@@ -437456,7 +437639,7 @@ export default registerPage(Page, { name: "${name18}" });
|
|
|
437456
437639
|
}));
|
|
437457
437640
|
};
|
|
437458
437641
|
handleAddEntity = async (payload) => {
|
|
437459
|
-
|
|
437642
|
+
this.sourceTracker?.addEntity({
|
|
437460
437643
|
scopeId: payload.scopeId,
|
|
437461
437644
|
entity: {
|
|
437462
437645
|
type: payload.type,
|
|
@@ -437470,7 +437653,7 @@ export default registerPage(Page, { name: "${name18}" });
|
|
|
437470
437653
|
});
|
|
437471
437654
|
};
|
|
437472
437655
|
handleUpdateEntity = async (payload) => {
|
|
437473
|
-
|
|
437656
|
+
this.sourceTracker?.updateEntity({
|
|
437474
437657
|
entityId: payload.entityId,
|
|
437475
437658
|
updates: payload.updates
|
|
437476
437659
|
});
|
|
@@ -437480,7 +437663,7 @@ export default registerPage(Page, { name: "${name18}" });
|
|
|
437480
437663
|
});
|
|
437481
437664
|
};
|
|
437482
437665
|
handleDeleteEntity = async (payload) => {
|
|
437483
|
-
const deletedEntityName =
|
|
437666
|
+
const deletedEntityName = this.sourceTracker?.deleteEntity({
|
|
437484
437667
|
entityId: payload.entityId
|
|
437485
437668
|
});
|
|
437486
437669
|
const changes = await this.sourceTracker?.getAndFlushChanges() ?? [];
|
|
@@ -437496,7 +437679,7 @@ export default registerPage(Page, { name: "${name18}" });
|
|
|
437496
437679
|
throw new Error("Root directory not set");
|
|
437497
437680
|
}
|
|
437498
437681
|
const filePath = path27.join(this.rootDir, APP_THEME_FILE_NAME);
|
|
437499
|
-
|
|
437682
|
+
this.sourceTracker?.updateTheme({
|
|
437500
437683
|
themeFilePath: filePath,
|
|
437501
437684
|
theme
|
|
437502
437685
|
});
|
|
@@ -437532,7 +437715,7 @@ export default registerPage(Page, { name: "${name18}" });
|
|
|
437532
437715
|
};
|
|
437533
437716
|
handleRenameEntity = async (payload) => {
|
|
437534
437717
|
const { elementId, newName, oldName, scopeName } = payload;
|
|
437535
|
-
|
|
437718
|
+
this.sourceTracker?.renameEntity({
|
|
437536
437719
|
entityId: elementId,
|
|
437537
437720
|
oldName,
|
|
437538
437721
|
newName,
|
|
@@ -438217,7 +438400,7 @@ var fileSyncVitePlugin = (pluginParams, options9) => {
|
|
|
438217
438400
|
const aiService = pluginParams.aiService;
|
|
438218
438401
|
const fsOperationQueue = pluginParams.fsOperationQueue;
|
|
438219
438402
|
const httpServer2 = pluginParams.httpServer;
|
|
438220
|
-
const fileSyncManager = new
|
|
438403
|
+
const fileSyncManager = new FileSystemManager(fsOperationQueue, generationNumberSequence, pluginParams.tracer);
|
|
438221
438404
|
if (syncService) {
|
|
438222
438405
|
syncService.generationNumberSequence = generationNumberSequence;
|
|
438223
438406
|
}
|
|
@@ -438630,7 +438813,12 @@ var fileSyncVitePlugin = (pluginParams, options9) => {
|
|
|
438630
438813
|
return socket.call.aiSetDraftState({ hasDraft });
|
|
438631
438814
|
});
|
|
438632
438815
|
});
|
|
438633
|
-
aiService.on("
|
|
438816
|
+
aiService.on("generationStarted", () => {
|
|
438817
|
+
fileSyncManager.enableOperationsQueue();
|
|
438818
|
+
});
|
|
438819
|
+
aiService.on("generationCompleted", async (hasDraft) => {
|
|
438820
|
+
fileSyncManager.disableOperationsQueue();
|
|
438821
|
+
await fileSyncManager.flushOperations();
|
|
438634
438822
|
if (hasDraft) {
|
|
438635
438823
|
logger3.info("Requesting full reload to render AI draft");
|
|
438636
438824
|
server.ws.send({
|
|
@@ -442607,7 +442795,7 @@ var import_util30 = __toESM(require_dist3(), 1);
|
|
|
442607
442795
|
// ../sdk/package.json
|
|
442608
442796
|
var package_default = {
|
|
442609
442797
|
name: "@superblocksteam/sdk",
|
|
442610
|
-
version: "2.0.6-next.
|
|
442798
|
+
version: "2.0.6-next.18",
|
|
442611
442799
|
type: "module",
|
|
442612
442800
|
description: "Superblocks JS SDK",
|
|
442613
442801
|
homepage: "https://www.superblocks.com",
|
|
@@ -442648,11 +442836,11 @@ var package_default = {
|
|
|
442648
442836
|
"@opentelemetry/semantic-conventions": "^1.28.0",
|
|
442649
442837
|
"@rollup/wasm-node": "^4.35.0",
|
|
442650
442838
|
"@superblocksteam/bucketeer-sdk": "0.5.0",
|
|
442651
|
-
"@superblocksteam/library": "2.0.6-next.
|
|
442652
|
-
"@superblocksteam/library-shared": "2.0.6-next.
|
|
442839
|
+
"@superblocksteam/library": "2.0.6-next.18",
|
|
442840
|
+
"@superblocksteam/library-shared": "2.0.6-next.18",
|
|
442653
442841
|
"@superblocksteam/shared": "0.9198.0",
|
|
442654
|
-
"@superblocksteam/util": "2.0.6-next.
|
|
442655
|
-
"@superblocksteam/vite-plugin-file-sync": "2.0.6-next.
|
|
442842
|
+
"@superblocksteam/util": "2.0.6-next.18",
|
|
442843
|
+
"@superblocksteam/vite-plugin-file-sync": "2.0.6-next.18",
|
|
442656
442844
|
"@vitejs/plugin-react": "^4.3.4",
|
|
442657
442845
|
axios: "^1.4.0",
|
|
442658
442846
|
chokidar: "^4.0.3",
|
|
@@ -450223,7 +450411,8 @@ async function startVite({ app, httpServer: httpServer2, root: root2, mode, port
|
|
|
450223
450411
|
};
|
|
450224
450412
|
const isCustomBuildEnabled2 = await isCustomComponentsEnabled();
|
|
450225
450413
|
const customFolder = path37.join(root2, "custom");
|
|
450226
|
-
const
|
|
450414
|
+
const draftsFolder = path37.join(root2, ".superblocks");
|
|
450415
|
+
const cdnUrl = "https://assets-cdn.superblocks.com/library/2.0.6-next.18";
|
|
450227
450416
|
const env3 = loadEnv(mode, root2, "");
|
|
450228
450417
|
const hmrPort = await getFreePort();
|
|
450229
450418
|
const hmrOptions = {
|
|
@@ -450254,10 +450443,7 @@ async function startVite({ app, httpServer: httpServer2, root: root2, mode, port
|
|
|
450254
450443
|
server: {
|
|
450255
450444
|
middlewareMode: true,
|
|
450256
450445
|
watch: {
|
|
450257
|
-
ignored: [
|
|
450258
|
-
`${customFolder}/**/*`,
|
|
450259
|
-
`${root2}/.superblocks/{generations,drafts}/**/*`
|
|
450260
|
-
]
|
|
450446
|
+
ignored: [`${customFolder}/**/*`, `${draftsFolder}/**/*`]
|
|
450261
450447
|
},
|
|
450262
450448
|
hmr: hmrOptions,
|
|
450263
450449
|
cors: {
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superblocksteam/cli",
|
|
3
|
-
"version": "2.0.6-next.
|
|
3
|
+
"version": "2.0.6-next.18",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Official Superblocks CLI",
|
|
6
6
|
"homepage": "https://www.superblocks.com",
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@eslint/js": "^9.16.0",
|
|
44
44
|
"@oclif/test": "^4.1.11",
|
|
45
|
-
"@superblocksteam/sdk": "2.0.6-next.
|
|
45
|
+
"@superblocksteam/sdk": "2.0.6-next.18",
|
|
46
46
|
"@superblocksteam/shared": "0.9198.0",
|
|
47
|
-
"@superblocksteam/util": "2.0.6-next.
|
|
47
|
+
"@superblocksteam/util": "2.0.6-next.18",
|
|
48
48
|
"@types/babel__core": "^7.20.0",
|
|
49
49
|
"@types/chai": "^4",
|
|
50
50
|
"@types/fs-extra": "^11.0.1",
|