feat(tapbundle_serverside): add network port discovery utilities and migrate file I/O to smartfs; refactor runtimes to use Node fs and SmartFs, update server APIs and bump dependencies

This commit is contained in:
2026-03-03 20:15:59 +00:00
parent 4d1896bdf9
commit f23c902658
24 changed files with 2562 additions and 2094 deletions

View File

@@ -3,6 +3,7 @@ import * as plugins from './plugins.js';
class TapNodeTools {
private smartshellInstance: plugins.smartshell.Smartshell;
private smartnetworkInstance: plugins.smartnetwork.SmartNetwork;
public testFileProvider = new TestFileProvider();
constructor() {}
@@ -87,12 +88,136 @@ class TapNodeTools {
public async createSmarts3() {
const smarts3Mod = await import('@push.rocks/smarts3');
const smarts3Instance = new smarts3Mod.Smarts3({
port: 3003,
cleanSlate: true,
server: { port: 3003 },
storage: { cleanSlate: true },
});
await smarts3Instance.start();
return smarts3Instance;
}
// ============
// Network Tools
// ============
private getSmartNetwork(): plugins.smartnetwork.SmartNetwork {
if (!this.smartnetworkInstance) {
this.smartnetworkInstance = new plugins.smartnetwork.SmartNetwork();
}
return this.smartnetworkInstance;
}
/**
* Find a single free port on the local machine.
*/
public async findFreePort(optionsArg?: {
startPort?: number;
endPort?: number;
randomize?: boolean;
exclude?: number[];
}): Promise<number> {
const options = {
startPort: 3000,
endPort: 60000,
randomize: true,
exclude: [] as number[],
...optionsArg,
};
const smartnetwork = this.getSmartNetwork();
const port = await smartnetwork.findFreePort(options.startPort, options.endPort, {
randomize: options.randomize,
exclude: options.exclude,
});
if (!port) {
throw new Error(
`Could not find a free port in range ${options.startPort}-${options.endPort}`
);
}
return port;
}
/**
* Find multiple distinct free ports on the local machine.
* Each found port is automatically excluded from subsequent searches.
*/
public async findFreePorts(countArg: number, optionsArg?: {
startPort?: number;
endPort?: number;
randomize?: boolean;
exclude?: number[];
}): Promise<number[]> {
const options = {
startPort: 3000,
endPort: 60000,
randomize: true,
exclude: [] as number[],
...optionsArg,
};
const smartnetwork = this.getSmartNetwork();
const ports: number[] = [];
const excluded = new Set(options.exclude);
for (let i = 0; i < countArg; i++) {
const port = await smartnetwork.findFreePort(options.startPort, options.endPort, {
randomize: options.randomize,
exclude: [...excluded],
});
if (!port) {
throw new Error(
`Could only find ${ports.length} of ${countArg} free ports in range ${options.startPort}-${options.endPort}`
);
}
ports.push(port);
excluded.add(port);
}
return ports;
}
/**
* Find a range of consecutive free ports on the local machine.
* All returned ports are sequential (e.g., [4000, 4001, 4002]).
*/
public async findFreePortRange(countArg: number, optionsArg?: {
startPort?: number;
endPort?: number;
exclude?: number[];
}): Promise<number[]> {
const options = {
startPort: 3000,
endPort: 60000,
exclude: [] as number[],
...optionsArg,
};
const smartnetwork = this.getSmartNetwork();
const excludeSet = new Set(options.exclude);
for (let start = options.startPort; start <= options.endPort - countArg + 1; start++) {
let allFree = true;
for (let offset = 0; offset < countArg; offset++) {
const port = start + offset;
if (excludeSet.has(port)) {
allFree = false;
start = port; // skip ahead past excluded port
break;
}
const isFree = await smartnetwork.isLocalPortUnused(port);
if (!isFree) {
allFree = false;
start = port; // skip ahead past occupied port
break;
}
}
if (allFree) {
const ports: number[] = [];
for (let offset = 0; offset < countArg; offset++) {
ports.push(start + offset);
}
return ports;
}
}
throw new Error(
`Could not find ${countArg} consecutive free ports in range ${options.startPort}-${options.endPort}`
);
}
}
export const tapNodeTools = new TapNodeTools();