Added resolvers, unified package versioning and author section to fit with mono repo style
This commit is contained in:
37
documentation/rule-processing/readme.md
Normal file
37
documentation/rule-processing/readme.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# @efforting.tech/rule-processing
|
||||||
|
|
||||||
|
Rule processing primitives
|
||||||
|
|
||||||
|
- [Source code](https://gitea.efforting.tech/efforting.tech/nodejs.esm-library/src/branch/main/source/rule-processing/resolver.mjs)
|
||||||
|
|
||||||
|
**TODO:** Write proper documentation for this module - currently just a sketch
|
||||||
|
|
||||||
|
## Class overview
|
||||||
|
|
||||||
|
<a name="clov-Abstract_Resolver"></a>
|
||||||
|
### Abstract_Resolver
|
||||||
|
|
||||||
|
Abstract base class that currently implements [`resolve()`](#f-Abstract_Resolver-resolve).
|
||||||
|
|
||||||
|
<a name="clov-Chained_Resolver"></a>
|
||||||
|
### Chained_Resolver
|
||||||
|
|
||||||
|
Goes through every link in the chain and uses the first handler that was resolved.
|
||||||
|
|
||||||
|
<a name="clov-Mapping_Resolver"></a>
|
||||||
|
### Mapping_Resolver
|
||||||
|
|
||||||
|
Resolves handlers by key, either derived via a key function or simply by the item itself as key.
|
||||||
|
|
||||||
|
## Classes
|
||||||
|
|
||||||
|
<a name="cl-Abstract_Resolver"></a>
|
||||||
|
### Abstract_Resolver
|
||||||
|
|
||||||
|
<a name="f-Abstract_Resolver-resolve"></a>
|
||||||
|
#### function `resolve`
|
||||||
|
```js
|
||||||
|
function resolve(item)
|
||||||
|
```
|
||||||
|
Resolves an item by calling the handler returned by the concrete [`resolve_handler()`](#function-resolve_handler) function.
|
||||||
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
"name": "experiments",
|
"name": "experiments",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@efforting.tech/errors": "link:../build/packages/errors"
|
"@efforting.tech/errors": "link:../build/packages/errors",
|
||||||
|
"@efforting.tech/rule-processing": "link:../build/packages/rule-processing"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,47 +1,6 @@
|
|||||||
import { Item_Unresolvable } from '@efforting.tech/errors';
|
import { Mapping_Resolver, Chained_Resolver } from '@efforting.tech/rule-processing/resolvers';
|
||||||
|
|
||||||
|
|
||||||
class Abstract_Resolver {
|
|
||||||
resolve(item) {
|
|
||||||
const handler = this.resolve_handler(item);
|
|
||||||
if (!handler) {
|
|
||||||
throw new Item_Unresolvable({ resolver: this, item });
|
|
||||||
}
|
|
||||||
return handler({ resolver: this, item });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Chained_Resolver extends Abstract_Resolver {
|
|
||||||
constructor(chain_links) {
|
|
||||||
super();
|
|
||||||
Object.assign(this, { chain_links });
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve_handler(item) {
|
|
||||||
const { chain_links } = this;
|
|
||||||
for (const link of chain_links) {
|
|
||||||
const handler = link.resolve_handler(item);
|
|
||||||
if (handler) {
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Mapping_Resolver {
|
|
||||||
constructor(rules=new Map(), key_function=null) {
|
|
||||||
Object.assign(this, { rules, key_function });
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve_handler(item) {
|
|
||||||
const { key_function, rules } = this;
|
|
||||||
const key = key_function ? key_function(item) : item;
|
|
||||||
return rules.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const vr = new Mapping_Resolver();
|
const vr = new Mapping_Resolver();
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
scope: '@efforting.tech'
|
scope: '@efforting.tech'
|
||||||
|
registry: 'https://npm.efforting.tech/'
|
||||||
|
version: 0.2.1
|
||||||
|
|
||||||
|
author:
|
||||||
|
name: 'Mikael Lövqvist'
|
||||||
|
email: 'mikael@efforting.tech'
|
||||||
|
url: 'https://gitea.efforting.tech/mikael-lovqvist'
|
||||||
|
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
@@ -6,18 +13,17 @@ packages:
|
|||||||
path: source/errors.mjs
|
path: source/errors.mjs
|
||||||
documentation: documentation/errors
|
documentation: documentation/errors
|
||||||
description: Library wide error definitions
|
description: Library wide error definitions
|
||||||
version: 0.1.2
|
|
||||||
|
rule-processing:
|
||||||
|
path: source/rule-processing
|
||||||
|
documentation: documentation/rule-processing
|
||||||
|
description: Rule based visitors, transformers, resolvers, processors, operators, aggregators and such.
|
||||||
|
internal-dependencies:
|
||||||
|
- errors
|
||||||
|
|
||||||
|
|
||||||
wip-packages:
|
wip-packages:
|
||||||
object-graph-storage:
|
object-graph-storage:
|
||||||
path: source/object-graph-storage
|
path: source/object-graph-storage
|
||||||
documentation: documentation/object-graph-storage
|
documentation: documentation/object-graph-storage
|
||||||
description: Lightweight persistent storage for structured data.
|
description: Lightweight persistent storage for structured data.
|
||||||
version: 0.1.0
|
|
||||||
|
|
||||||
rule-processing:
|
|
||||||
path: source/rule-processing
|
|
||||||
documentation: documentation/rule-processing
|
|
||||||
description: Rule based visitors, transformers, resolvers, processors, operators, aggregators and such.
|
|
||||||
version: 0.1.0
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> This document is written by Claude by Anthropic using Sonnet 4.6 and has yet to be vetted by Mikael Lövqvist
|
> This document is written by Claude by Anthropic using Sonnet 4.6 and has yet to be vetted by Mikael Lövqvist
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Insertion order of Set and Map are guaranteed. See https://tc39.es/ecma262/multipage/keyed-collections.html#sec-set.prototype.foreach and https://tc39.es/ecma262/multipage/keyed-collections.html#sec-map.prototype.foreach
|
||||||
|
> This document should be updated with this information.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
A reusable, minimum-footprint storage component written in Node.js, intended as a foundation across multiple projects. Backends for other languages are viable as long as they can serialize to JSON. The design aims to address [ACID](https://en.wikipedia.org/wiki/ACID) guarantees while keeping the implementation surface manageable.
|
A reusable, minimum-footprint storage component written in Node.js, intended as a foundation across multiple projects. Backends for other languages are viable as long as they can serialize to JSON. The design aims to address [ACID](https://en.wikipedia.org/wiki/ACID) guarantees while keeping the implementation surface manageable.
|
||||||
|
|||||||
43
source/rule-processing/resolvers.mjs
Normal file
43
source/rule-processing/resolvers.mjs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { Item_Unresolvable } from '@efforting.tech/errors';
|
||||||
|
|
||||||
|
export class Abstract_Resolver {
|
||||||
|
resolve(item) {
|
||||||
|
const handler = this.resolve_handler(item);
|
||||||
|
if (!handler) {
|
||||||
|
throw new Item_Unresolvable({ resolver: this, item });
|
||||||
|
}
|
||||||
|
return handler({ resolver: this, item });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Chained_Resolver extends Abstract_Resolver {
|
||||||
|
constructor(chain_links) {
|
||||||
|
super();
|
||||||
|
Object.assign(this, { chain_links });
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_handler(item) {
|
||||||
|
const { chain_links } = this;
|
||||||
|
for (const link of chain_links) {
|
||||||
|
const handler = link.resolve_handler(item);
|
||||||
|
if (handler) {
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class Mapping_Resolver extends Abstract_Resolver {
|
||||||
|
constructor(rules=new Map(), key_function=null) {
|
||||||
|
Object.assign(this, { rules, key_function });
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_handler(item) {
|
||||||
|
const { key_function, rules } = this;
|
||||||
|
const key = key_function ? key_function(item) : item;
|
||||||
|
return rules.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { parse as parse_yaml, stringify as format_yaml } from 'yaml';
|
import { parse as parse_yaml, stringify as format_yaml } from 'yaml';
|
||||||
import { unlinkSync, linkSync, chmodSync, readFileSync, writeFileSync, readdirSync, mkdirSync, symlinkSync, statSync, readlinkSync } from 'node:fs';
|
import { unlinkSync, linkSync, chmodSync, readFileSync, writeFileSync, readdirSync, mkdirSync, statSync, readlinkSync } from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
const [manifest_file, source, output_directory] = process.argv.slice(2);
|
const [manifest_file, source, output_directory] = process.argv.slice(2);
|
||||||
@@ -7,49 +7,6 @@ const manifest = parse_yaml(readFileSync(manifest_file, 'utf-8'));
|
|||||||
|
|
||||||
//TODO: We must test this with a non-single-file type package
|
//TODO: We must test this with a non-single-file type package
|
||||||
|
|
||||||
|
|
||||||
//NOTE: For now we will simply symlink files over from source which is fine as long as we are
|
|
||||||
// not preprocecessing source, in that case symlinks would be from the processed items
|
|
||||||
// and rapid iteration becomes trickier,
|
|
||||||
// though of course we can hook up make to a file event watcher.
|
|
||||||
function symlink_tree(src, dest) {
|
|
||||||
mkdirSync(dest, { recursive: true });
|
|
||||||
if (statSync(src).isDirectory()) {
|
|
||||||
for (const entry of readdirSync(src, { withFileTypes: true })) {
|
|
||||||
|
|
||||||
throw new Error('This branch is clanker-suggested and wanted to do absolute symlinks - fix it when needed')
|
|
||||||
|
|
||||||
const src_path = path.resolve(join(src, entry.name));
|
|
||||||
const dest_path = path.join(dest, entry.name);
|
|
||||||
if (entry.isDirectory()) {
|
|
||||||
symlink_tree(src_path, dest_path);
|
|
||||||
} else {
|
|
||||||
console.log('NOT IMPLEMENTED SYMLINK', src_path, dest_path);
|
|
||||||
//symlinkSync(src_path, dest_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const symlink_dest = path.join(dest, path.basename(src));
|
|
||||||
const symlink_src = path.relative(dest, src);
|
|
||||||
//console.log('SYMLINK', symlink_src, symlink_dest);
|
|
||||||
//TODO: make a utility function for this
|
|
||||||
try {
|
|
||||||
symlinkSync(symlink_src, symlink_dest);
|
|
||||||
} catch (e) {
|
|
||||||
if (e.code === 'EEXIST') {
|
|
||||||
if (readlinkSync(symlink_dest) !== symlink_src) {
|
|
||||||
unlinkSync(symlink_dest);
|
|
||||||
symlinkSync(symlink_src, symlink_dest);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [symlink_dest];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function link_tree(src, dest) {
|
function link_tree(src, dest) {
|
||||||
mkdirSync(dest, { recursive: true });
|
mkdirSync(dest, { recursive: true });
|
||||||
if (statSync(src).isDirectory()) {
|
if (statSync(src).isDirectory()) {
|
||||||
@@ -79,7 +36,7 @@ function link_tree(src, dest) {
|
|||||||
} else {
|
} else {
|
||||||
const link_dest = path.join(dest, path.basename(src));
|
const link_dest = path.join(dest, path.basename(src));
|
||||||
const link_src = src;
|
const link_src = src;
|
||||||
//console.log('SYMLINK', link_src, link_dest);
|
//console.log('LINK', link_src, link_dest);
|
||||||
//TODO: make a utility function for this
|
//TODO: make a utility function for this
|
||||||
try {
|
try {
|
||||||
linkSync(link_src, link_dest);
|
linkSync(link_src, link_dest);
|
||||||
@@ -99,26 +56,34 @@ const workspace_manifest = {
|
|||||||
packages: [],
|
packages: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const root_package = {
|
const { scope, registry, author, version } = manifest;
|
||||||
name: path.join(manifest.scope, 'root'),
|
|
||||||
version: '0.1.0', //TODO: Not hardcode? What does this version even represent?
|
const common_package_data = {
|
||||||
|
author,
|
||||||
|
version,
|
||||||
type: 'module',
|
type: 'module',
|
||||||
dependencies: {},
|
|
||||||
publishConfig: {
|
publishConfig: {
|
||||||
registry: 'https://npm.efforting.tech/', //TODO: Get from manifest
|
registry
|
||||||
},
|
},
|
||||||
author: 'mikael-lovqvist', //TODO: Get from manifest
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const root_package = {
|
||||||
|
name: path.join(scope, 'root'),
|
||||||
|
dependencies: {},
|
||||||
|
...common_package_data,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
for (const [package_name, package_data] of Object.entries(manifest.packages)) {
|
for (const [package_name, package_data] of Object.entries(manifest.packages)) {
|
||||||
const pkg = { name: package_name, ...package_data };
|
const pkg = { name: package_name, ...package_data };
|
||||||
|
|
||||||
const pkg_scope_path = path.join(manifest.scope, pkg.name);
|
const pkg_scope_path = path.join(scope, pkg.name);
|
||||||
workspace_manifest.packages.push(pkg.name);
|
workspace_manifest.packages.push(pkg.name);
|
||||||
|
|
||||||
const pkg_dir = path.join(output_directory, pkg.name);
|
const pkg_dir = path.join(output_directory, pkg.name);
|
||||||
const linked_sources = link_tree(pkg.path, pkg_dir).map(p => path.relative(pkg_dir, p));
|
const linked_sources = link_tree(pkg.path, pkg_dir).map(p => path.relative(pkg_dir, p));
|
||||||
const linked_docs = link_tree(pkg.documentation, pkg_dir).map(p => path.relative(pkg_dir, p));
|
// Docs are optional for now
|
||||||
|
const linked_docs = pkg.documentation ? link_tree(pkg.documentation, pkg_dir).map(p => path.relative(pkg_dir, p)) : [];
|
||||||
//console.log('DOCS', { linked_docs });
|
//console.log('DOCS', { linked_docs });
|
||||||
|
|
||||||
const exports_map = {};
|
const exports_map = {};
|
||||||
@@ -128,16 +93,12 @@ for (const [package_name, package_data] of Object.entries(manifest.packages)) {
|
|||||||
exports_map[key] = `./${file}`;
|
exports_map[key] = `./${file}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { version, description } = pkg;
|
const { description } = pkg;
|
||||||
const pkg_json = JSON.stringify({
|
const pkg_json = JSON.stringify({
|
||||||
name: pkg_scope_path,
|
name: pkg_scope_path,
|
||||||
version, description,
|
description,
|
||||||
type: 'module',
|
|
||||||
exports: exports_map,
|
exports: exports_map,
|
||||||
author: 'mikael-lovqvist', //TODO: Get from manifest
|
...common_package_data,
|
||||||
publishConfig: {
|
|
||||||
registry: 'https://npm.efforting.tech/', //TODO: Get from manifest
|
|
||||||
},
|
|
||||||
}, null, ' ');
|
}, null, ' ');
|
||||||
|
|
||||||
writeFileSync(path.join(pkg_dir, 'package.json'), pkg_json, 'utf-8');
|
writeFileSync(path.join(pkg_dir, 'package.json'), pkg_json, 'utf-8');
|
||||||
|
|||||||
Reference in New Issue
Block a user