Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@nuwainfo/ffl-js

Dependency-free Node.js binding for FastFileLink. The npm package bundles the portable ffl.com APE, so callers do not need a separate FFL installation or Python runtime.

The low-level command grammar is generated by APEBind from binding/ffl.apebind.yaml. The public API in src/ffl/client.js is handwritten so FFL-specific library semantics remain explicit and reviewable.

Runtime requirements

  • Node.js 18 or newer
  • An environment where child processes can execute the bundled APE

There are no runtime npm dependencies.

CLI

The package also exposes the bundled FFL command-line program. Run it without a project installation:

npx @nuwainfo/ffl-js --version

After npm install @nuwainfo/ffl-js, run the project-local command with:

npx --no-install ffl-js --version

Usage

import { share } from '@nuwainfo/ffl-js';

const session = await share('release.zip', {
  maxDownloads: 1,
  timeoutSeconds: 1800,
});

console.log(session.link);
await session.stop();

Multiple files can be shared directly:

const session = await share(['one.txt', 'two.txt'], {
  name: 'files.zip',
});

Text and bytes helpers own their temporary source until the share session closes:

import { shareText } from '@nuwainfo/ffl-js';

const session = await shareText('hello', { name: 'hello.txt' });
console.log(session.link);
await session.close();

Optional-value FFL flags use natural JavaScript values. receipt: true emits --receipt without a value, while receipt: 'me@example.com' emits the flag and address.

Streams and events

Use shareStream() to send a Node Readable directly to FFL without creating a temporary file:

import { createReadStream } from 'node:fs';
import { shareStream } from '@nuwainfo/ffl-js';

const session = await shareStream(createReadStream('database.sql'), {
  name: 'database.sql',
});

session.on('ready', (event) => console.log(event.data));
session.on('progress', (event) => console.log(event.data));

Semantic event names are ready, progress, transport, and completed. For FFL protocol diagnostics, use session.onRaw('/hook/...', listener) or session.rawEvents(). Hook servers and temporary sources are released when the APE process ends, even if the caller does not explicitly close the session.

Download

import { download } from '@nuwainfo/ffl-js';

const result = await download('https://example.fastfilelink/...', {
  outputPath: 'download.bin',
});

console.log(result.outputPath);
console.log(result.transferMode);

download() preserves the underlying process result while exposing parsed transfer mode and output path information.

For cancellation or binary streaming, use a download session:

import { downloadStream, startDownload } from '@nuwainfo/ffl-js';

const controller = new AbortController();
const transfer = await startDownload('https://example.fastfilelink/...', {
  outputPath: 'download.bin',
  signal: controller.signal,
});

await transfer.done;

const streamed = await downloadStream('https://example.fastfilelink/...');
streamed.stdout.pipe(process.stdout);
await streamed.done;

downloadStream() preserves arbitrary bytes from FFL stdout; it does not decode the download payload as UTF-8.

Authentication secrets

For shares protected with HTTP Basic Auth, set FFL_AUTH_PASSWORD in the application environment and pass only authUser. The bundled FFL subprocess inherits the environment, so the password stays out of its command line:

export FFL_AUTH_PASSWORD='use-your-secret-manager'
$env:FFL_AUTH_PASSWORD = 'use-your-secret-manager'
const session = await share('release.zip', { authUser: 'deploy' });

Do not also pass authPassword: FFL gives the explicit CLI option precedence over FFL_AUTH_PASSWORD. The environment variable applies to the sharing side; pass download credentials explicitly when downloading a protected link.

WSL2

If an operation fails with TLSError([0x6300]), WSL may be routing the bundled .com APE through Windows interop. Run the following in WSL, then restart the WSL session:

sudo sh -c 'echo -1 > /proc/sys/fs/binfmt_misc/WSLInterop'

Key generation

import { keygen } from '@nuwainfo/ffl-js';

const result = await keygen('alice');
console.log(result.publicKeyPath);
console.log(result.privateKeyPath);

Version and raw access

import { raw, version } from '@nuwainfo/ffl-js';

console.log(await version());
const result = await raw(['download', '--help']);

Development

The repository is a normal npm source project. The .tgz produced by npm pack is a build artifact, not the source of truth.

npm test
npm run test:packaging
npm run test:real
npm pack

The test groups are kept separate because the real and packaging suites execute the bundled APE.

Updating the generated binding

Install the matching APEBind source project. When adopting a new ffl.com, first refresh the raw CLI discovery evidence:

python scripts/inspect.py --ape /path/to/ffl.com

Review binding/ffl.discovered.apebind.yaml, then manually merge intentional changes into the canonical semantic contract binding/ffl.apebind.yaml. Automatic inspection never overwrites the semantic contract.

Regenerate only the machine-owned low-level files:

python scripts/regenerate.py --ape /path/to/ffl.com

Regeneration updates:

  • src/ffl/_generated.js
  • src/ffl/_generated.d.ts
  • src/ffl/_runtime.js
  • src/ffl/_runtime.d.ts
  • src/ffl/bin/ffl.com

It deliberately does not overwrite the handwritten client, result parsers, public type declarations, tests, or semantic schema.

About

Javascript binding for ffl (FastFileLink CLI)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages