lang-mini
v0.0.46
Published
General purpose JavaScript functions, extracted from jsgui. Also includes Evented_Class
Readme
lang-mini
Lang-mini is a lightweight, zero-runtime-dependency JavaScript toolkit that powers the core utilities, type system, and event infrastructure used across the wider jsgui3 ecosystem. It ships as a single file (lang-mini.js), works identically in Node.js and modern browsers, and focuses on highly-polymorphic utilities that remain ergonomic in plain JavaScript.
✨ Key idea: Lang-mini combines small, composable helpers (iteration, cloning, truth maps) with advanced primitives (multi-function polymorphism, functional data types, grammar-based type inference) so that larger frameworks can build rich behavior without carrying heavy dependencies.
Why lang-mini?
- Cross-environment –
lang-mini.jsauto-detects whether it's running in Node or the browser and exports the same API in both contexts vialib-lang-mini.js. - Polymorphic by default –
mfp()andfp()route calls using runtime signatures, letting you author concise yet type-aware functions. - Typed without TypeScript –
Functional_Data_Type,Type_Signifier, andGrammarprovide runtime validation, parsing, and structural signatures that plug into UI and data layers. - Event-first architecture –
Evented_Class,eventify,field, andpropenable reactive data binding with tiny objects. - Battle-tested – A combined Jest + legacy test suite (118+ assertions) covers the core behaviors and prevents regressions.
Table of contents
- Quick start
- Installation
- Core concepts
- Reactive models & events
- Detailed feature guide
- API highlights
- Usage patterns & examples
- Asynchronous coordination
- Project layout
- Testing & development
- Status & roadmap notes
- License
Quick start
// CommonJS (Node.js ≥ 15)
const lang = require('lang-mini');
const { each, mfp } = lang;
each([1, 2, 3], (value, index) => {
console.log(index, value * 2);
});
const describe = mfp({ name: 'describe', return_type: 'string' }, {
's': (str) => `string(${str.length})`
});
console.log(describe('hello'));// ESM / bundlers (Vite, Webpack, etc.)
import lang from 'lang-mini';
const pointDistance = lang.distance_between_points([[0, 0], [3, 4]]);
console.log(pointDistance); // → 5<!-- Browser (UMD-style) -->
<script src="/path/to/lang-mini.js"></script>
<script>
const { each, eventify } = window.lang;
const state = eventify({});
each(['a', 'b'], (item) => console.log(item));
</script>Installation
Lang-mini is published to npm as a CommonJS module with no production dependencies.
npm install lang-miniNode / bundlers
- Entry point:
lib-lang-mini.jsre-exports everything fromlang-mini.js. - Works with Node.js ≥ 15 (per
package.jsonenginesfield). - When using ESM, import the default export:
import lang from 'lang-mini';. - Tree-shaking is limited because the build is a single module; destructure the functions you need.
Browser via script tag
- Copy
lang-mini.jsinto your project or serve it from your build pipeline. - Include it before your scripts. It attaches a
langglobal. - For bundlers, treat it as a standard CommonJS module and rely on your bundler's CJS support.
Core concepts
Collection helpers
each(collection, fn, [context])– Unified iterator for arrays and objects. Returns a new array/object of collected results. Supports early exit via a thirdstopcallback parameter.clone(value)– Shallow clone for primitives, arrays, and objects. (Deep cloning is out of scope.)arr_like_to_arr(arrayLike)– Convertsarguments, DOM collections, and other array-like objects into real arrays.- Truth-map utilities:
get_truth_map_from_arr(array)converts an array into{ value: true }object.get_arr_from_truth_map(map)converts it back (stringifying keys).get_map_from_arr(array)indexes values to their latest position.
is_array(value)andis_defined(value)provide robust guards that match the library's type semantics.
Functional polymorphism
mfp(config, handlers)– Multi-function polymorphism. Dispatches on signature strings built from runtime argument types (e.g.'n,n','s,[n]'). Supports verbs, nouns, and grammar metadata for descriptive error messages.fp(handlers)– Lightweight version for simple polymorphic dispatch.vectorify(fn)– Lifts a binary numeric function so it works element-wise over vectors/arrays.distance_between_points([[x1, y1], [x2, y2]])– Example of a vector-aware helper included in the library.
Type detection & signatures
tof(value)– Enhancedtypeofreturningarray,null,undefined, etc.tf(value)– Abbreviated signatures ('n','s','b','a','o','u','N'). Used internally bymfp.deep_sig(value, [maxDepth])– Generates deterministic structural signatures such as'[{"a":n},{"b":n}]'. Useful for memoization and grammar inference.
Runtime type system
Functional_Data_Type– Define runtime types withvalidate, optionalparse_string, and metadata. Frequently combined withfieldfor model validation.Type_Signifier&Type_Representation– Experimental utilities (exported vialib-lang-mini.js) for distinguishing what a value is from how it is represented. Useful when modelling complex domains (colors, dates, binary blobs).
Events & data binding
Evented_Class– Minimal event emitter with.on(event, handler)and.raise(event, data).eventify(obj)– Mixes event methods into plain objects so they can emit (raise) and listen (on) to events.field(obj, name, [typeOrDefault], [defaultOrTransform])– Declares data-bound properties on eventified objects. Supports:- default values (
field(obj, 'age', 21)) - validation via
Functional_Data_Type - transformation functions (
field(obj, 'upperName', value => value.toUpperCase())) - change notifications (
obj.raise('change', { name, old, value })).
- default values (
prop– For binding properties across objects (used alongsideeventify).
⚠️ Requirement: Always pass an object through
eventify()before callingfield()orprop(). Otherwise aTypeErroris thrown (raise is not a function).
Grammar-driven inference (WIP)
Grammar– Describes structured data with singular/plural forms and resolves runtime signatures.grammar.tof(value)– Infers the matching definition name (e.g.'user_login').grammar.sig(value)– Resolves plural forms ('users','locations').- Some deeper methods in
Grammarare still markedNYI; expect evolution in future releases.
Reactive models & events
Reactive behavior is built into the library through Evented_Class, eventify, field, prop, and Functional_Data_Type. The combination lets you turn plain objects into observable models, add typed fields, and hook change events without pulling in a heavier framework. Check docs/Reactive_Models.md for the full guide and usage examples.
Detailed feature guide
The helpers highlighted under “core concepts” are unpacked at docs/Core_Features.md. That guide includes short examples for the collection utilities, polymorphic dispatch helpers, signature detectors, runtime type layer, and grammar/combinator building blocks.
API highlights
| Area | Key exports | Notes |
|------|-------------|-------|
| Collections | each, clone, arr_like_to_arr, get_truth_map_from_arr, get_map_from_arr, get_arr_from_truth_map | Consistent semantics between arrays and objects; helpers return transformed copies. |
| Type detection | tof, tf, deep_sig | Signatures integrate with grammar and polymorphism features. |
| Functional utilities | mfp, fp, vectorify, distance_between_points | mfp supports default handlers ('default' key) and grammar-aware error messages. |
| Events | Evented_Class, eventify | Underpins field and prop; Evented_Class can be subclassed for richer components. |
| Data binding | field, prop, Functional_Data_Type | Compose validation, parsing, defaults, and transformations with change events. |
| Advanced types | Type_Signifier, Type_Representation | Experimental building blocks for rich type metadata. |
| Combinatorics | combinations (alias: combos) | Cartesian product across nested arrays; stops early if any sub-array is empty. |
| Async coordination | call_multi, call_multiple_callback_functions, Fns, Publisher | Mix callback ergonomics with promise-friendly utilities and readiness helpers. |
Full source documentation lives inline within lang-mini.js; search for function names to see implementation notes and historical commentary.
Usage patterns & examples
Iterating with each
const { each } = require('lang-mini');
const labelled = each({ a: 1, b: 2 }, (value, key) => `${key}:${value}`);
// labelled → { a: 'a:1', b: 'b:2' }
const doubleUp = each([1, 2, 3], (value, index, stop) => {
if (value === 3) stop();
return value * 2;
});
// doubleUp → [2, 4]Polymorphism with mfp
const { mfp } = require('lang-mini');
const sum = mfp({ name: 'sum' }, {
'n,n': (a, b) => a + b,
'[n]': (arr) => arr.reduce((acc, n) => acc + n, 0),
'default': (_, signature) => {
throw new TypeError(`sum has no handler for signature ${signature}`);
}
});
sum(2, 3); // → 5
sum([1, 2, 3, 4]); // → 10Creating validated, reactive models
const lang = require('lang-mini');
const person = lang.eventify({});
const IntegerType = new lang.Functional_Data_Type({
name: 'integer',
validate: Number.isInteger,
parse_string: (value) => {
const parsed = Number(value);
return Number.isInteger(parsed) ? parsed : undefined;
}
});
lang.field(person, 'name', (value) => value.trim());
lang.field(person, 'age', IntegerType, 18);
person.on('change', ({ name, old, value }) => {
console.log(`${name} changed from ${old} → ${value}`);
});
person.name = ' Ada ';
person.age = '21';
// Console:
// name changed from undefined → Ada
// age changed from 18 → 21Working with combinations
const { combinations } = require('lang-mini');
const colourways = combinations([
['red', 'green'],
['S', 'M', 'L'],
['cotton', 'linen']
]);
console.log(colourways.length); // 12Grammar-powered inference
const { Grammar } = require('lang-mini');
const geoGrammar = new Grammar({
name: 'Geo',
def: {
coordinate: { def: ['number', 'number'], plural: 'coordinates' },
route: { def: ['coordinate', 'coordinate'], plural: 'routes' }
}
});
geoGrammar.tof([51.5, -0.1]); // → 'coordinate'
geoGrammar.sig([[51.5, -0.1], [40.7, -74]]); // → 'route'
geoGrammar.sig([ [ [0,0], [1,1] ], [ [2,2], [3,3] ] ]); // → 'routes'Tip: Some advanced grammar APIs remain marked
NYI; consult inline comments before relying on them in production.
Asynchronous coordination
Lang-mini bundles a tiny but expressive concurrency helper stack for orchestrating callback-heavy workflows without pulling in a promise library. The primitives mirror the asynchronous utilities used throughout the historical jsgui projects and are now fully documented in docs/Async_Coordination.md.
call_multi & call_multiple_callback_functions
- Accepts an array of work items where each item can be a bare function,
[fn, params],[context, fn], or[fn, params, perTaskCallback]. - Optional flags support:
- Parallelism: pass
lang.call_multi(tasks, parallelism, done)to limit concurrency. - Delays: pass both
parallelismanddelayto stagger invocations when throttling external systems. - Parameter echoing:
lang.call_multi(tasks, done, true)returns[[params], result]tuples so you can rebuild association tables after asynchronous fan-outs.
- Parallelism: pass
- Errors short-circuit the execution and bubble through the final callback, making it easy to surface failures while still allowing per-task callbacks to run.
const lang = require('lang-mini');
const work = [
(cb) => setTimeout(() => cb(null, 'A'), 5),
[({ message }, cb) => cb(null, message.toUpperCase()), [{ message: 'b' }]],
[
(value, cb) => cb(null, value * 2),
[21],
(err, doubled) => console.log('per-task:', doubled)
]
];
lang.call_multi(work, 2, 1, (err, results) => {
if (err) throw err;
console.log(results); // → ['A', { message: 'B' }, 42]
});Fns.go
Fns(arr) wraps an array of functions and exposes a .go() helper that forwards to call_multi, preserving the same argument permutations. This is particularly useful when dynamically building pipelines:
const steps = lang.Fns([
(cb) => readFile('./input.json', cb),
(contents, cb) => cb(null, JSON.parse(contents)),
(data, cb) => cb(null, transform(data))
]);
steps.go((err, [buffer, parsed, transformed]) => {
if (err) throw err;
console.log(transformed);
});Publisher
Publisher extends Evented_Class and adds a when_ready getter that returns a promise resolving once the instance raises ready. The helper caches readiness so subsequent calls resolve synchronously, letting you bridge older callback code with modern async/await:
const publisher = new lang.Publisher();
async function ensureReady() {
await publisher.when_ready; // waits until publisher.raise('ready')
console.log('ready to emit');
}
ensureReady();
setTimeout(() => publisher.raise('ready'), 20);Project layout
lang-mini/
├─ lang-mini.js # Single-file implementation (~2800 lines)
├─ lib-lang-mini.js # CommonJS re-export for consumers
├─ examples/ # Runnable usage samples (Node scripts)
├─ docs/ # Additional design notes (e.g., Control_Dom)
├─ tests/ # Jest + legacy test suites
│ ├─ *.test.js # Modern Jest suites
│ ├─ all-test.js # Legacy runner
│ └─ new-tests.js # Legacy runner extension
├─ AI-NOTES.md # Development notes and open questions
├─ TEST-SUMMARY.md # Snapshot of recent test runs
└─ package.json # Metadata, scripts, dev dependencies (jest, fnl)Testing & development
Run the full suite (Jest + legacy tests):
npm testRun only the Jest suites:
npm run test:jestRun legacy suites (useful when iterating on old tests):
npm run test:legacyContribution tips
- Keep
lang-mini.jschanges focused; it's a shared dependency across multiple projects. - Document behavioral observations or known issues in
AI-NOTES.mdbefore making large refactors. - Add or update tests under
tests/and ensurenpm testpasses before submitting PRs.
Status & roadmap notes
- Version
0.0.40(seepackage.json). - No production dependencies;
fnlandjestare dev-only. - Several
Grammarand advanced type-system methods are intentionally markedNYI; contributions welcome but coordinate viaAI-NOTES.mdfirst. - Roadmap items such as richer type representations and additional documentation live in
AI-README.mdandroadmap.md. - Keep up with the current focus areas in
docs/Whats_Next.md, which consolidates the remaining gaps and suggested next steps for lang-mini.
License
MIT © James Vickers / Metabench
