Comparing version 11.0.0 to 12.0.0
202
lib/main.js
(function() { | ||
'use strict'; | ||
var Datom, GUY, MAIN, alert, assign, debug, echo, get_base_types, help, info, inspect, letsfreezethat, letsfreezethat_nofreeze, log, p1, p2, plain, praise, rpr, urge, warn, whisper, | ||
indexOf = [].indexOf; | ||
var Datom, GUY, MAIN, alert, assign, debug, echo, get_base_types, help, info, inspect, letsfreezethat, letsfreezethat_nofreeze, log, matcher_cache, plain, praise, rpr, urge, warn, whisper; | ||
@@ -22,9 +21,4 @@ //########################################################################################################### | ||
//=========================================================================================================== | ||
// SELECT | ||
//----------------------------------------------------------------------------------------------------------- | ||
p1 = /^(?<skey>(?<sigil>[<^>\[~\]\x23])(?<key>[^<^>\[~\]\x23]*))$/u; // `\x23` used instead of `\#` which causes syntax error (???) | ||
matcher_cache = new Map(); | ||
p2 = /^(?<skey>(?<sigil>[<^>\[~\]\x23])(?<key>[^<^>\[~\]\x23]*))\x23(?<attribute>[^<^>\[~\]\x23]+):(?<value>[^<^>\[~\]\x23]+)$/u; // `\x23` used instead of `\#` which causes syntax error (???) | ||
//=========================================================================================================== | ||
@@ -49,4 +43,2 @@ // EXPORT | ||
//--------------------------------------------------------------------------------------------------------- | ||
this.is_system = this.is_system.bind(this); | ||
//--------------------------------------------------------------------------------------------------------- | ||
this.is_stamped = this.is_stamped.bind(this); | ||
@@ -59,25 +51,4 @@ this.is_fresh = this.is_fresh.bind(this); | ||
//--------------------------------------------------------------------------------------------------------- | ||
this._new_datom = this._new_datom.bind(this); | ||
this.new_fresh_datom = this.new_fresh_datom.bind(this); | ||
//--------------------------------------------------------------------------------------------------------- | ||
this.fresh_datom = this.fresh_datom.bind(this); | ||
//--------------------------------------------------------------------------------------------------------- | ||
this.wrap_datom = this.wrap_datom.bind(this); | ||
//--------------------------------------------------------------------------------------------------------- | ||
this.new_single_datom = this.new_single_datom.bind(this); | ||
this.new_open_datom = this.new_open_datom.bind(this); | ||
this.new_close_datom = this.new_close_datom.bind(this); | ||
this.new_system_datom = this.new_system_datom.bind(this); | ||
this.new_text_datom = this.new_text_datom.bind(this); | ||
// @new_flush_datom = -> @new_system_datom 'flush' | ||
//--------------------------------------------------------------------------------------------------------- | ||
this.new_warning = this.new_warning.bind(this); | ||
//========================================================================================================= | ||
//--------------------------------------------------------------------------------------------------------- | ||
/* TAINT likely to be removed */ | ||
this.new_xemitter = this.new_xemitter.bind(this); | ||
//========================================================================================================= | ||
//--------------------------------------------------------------------------------------------------------- | ||
this.select = this.select.bind(this); | ||
@@ -90,2 +61,3 @@ // super() | ||
this.thaw = this.LFT.thaw; | ||
GUY.props.hide(this, 'matcher_cache', matcher_cache); | ||
return void 0; | ||
@@ -120,7 +92,7 @@ } | ||
return this.lets(d, function(d) { | ||
var ref1, results, v; | ||
ref1 = assign({}, k, ...P); | ||
var ref, results, v; | ||
ref = assign({}, k, ...P); | ||
results = []; | ||
for (k in ref1) { | ||
v = ref1[k]; | ||
for (k in ref) { | ||
v = ref[k]; | ||
results.push(d[k] = v); | ||
@@ -139,3 +111,3 @@ } | ||
stamp(d, ...P) { | ||
/* Set the `$stamped` attribute on datom to sigil it as processed. Stamped datoms will not be selected | ||
/* Set the `$stamped` attribute on datom to mark it as processed. Stamped datoms will not be selected | ||
by the `select` method unless tag '#stamped' is used. */ | ||
@@ -158,20 +130,15 @@ return this.lets(d, function(d) { | ||
is_system(d) { | ||
/* Return whether datom is a system datom (i.e. whether its `sigil` equals `'~'`). */ | ||
return d.$key.match(/^[~\[\]]/); | ||
} | ||
is_stamped(d) { | ||
var ref1; | ||
return (ref1 = d.$stamped) != null ? ref1 : false/* i.e. already processed? */; | ||
var ref; | ||
return (ref = d.$stamped) != null ? ref : false/* i.e. already processed? */; | ||
} | ||
is_fresh(d) { | ||
var ref1; | ||
return (ref1 = d.$fresh) != null ? ref1 : false/* i.e. created within stream? */; | ||
var ref; | ||
return (ref = d.$fresh) != null ? ref : false/* i.e. created within stream? */; | ||
} | ||
is_dirty(d) { | ||
var ref1; | ||
return (ref1 = d.$dirty) != null ? ref1 : false/* i.e. modified? */; | ||
var ref; | ||
return (ref = d.$dirty) != null ? ref : false/* i.e. modified? */; | ||
} | ||
@@ -183,32 +150,8 @@ | ||
new_datom($key, $value, ...other) { | ||
/* When `other` contains a key `$`, it is treated as a hint to copy | ||
system-level attributes; if the value of key `$` is a POD that has itself a | ||
key `$`, then a copy of that value is used. This allows to write `new_datom | ||
..., $: d` to copy system-level attributes such as source locations to a new | ||
datom. */ | ||
new_datom($key, ...P) { | ||
this.types.validate.datom_key($key); | ||
return this._new_datom($key, $value, ...other); | ||
return this.freeze(assign({}, ...P, {$key})); | ||
} | ||
_new_datom($key, $value, ...other) { | ||
var R; | ||
if ($value != null) { | ||
if ((!this.cfg.merge_values) || (!this.types.isa.object($value))) { | ||
$value = {$value}; | ||
} | ||
if (indexOf.call(Object.keys($value), '$key') >= 0) { | ||
throw new Error("µ55632 value must not have attribute '$key'"); | ||
} | ||
R = assign({}, $value, ...other, {$key}); | ||
} else { | ||
R = assign({}, ...other, {$key}); | ||
} | ||
while ((this.types.isa.object(R.$)) && (this.types.isa.object(R.$.$))) { | ||
R.$ = this.LFT._deep_copy(R.$.$); | ||
} | ||
return this.freeze(R); | ||
} | ||
fresh_datom(...P) { | ||
new_fresh_datom(...P) { | ||
return this.new_datom(...P, { | ||
@@ -219,47 +162,25 @@ $fresh: true | ||
wrap_datom($key, $value) { | ||
this.types.validate.datom_key($key); | ||
this.types.validate.datom_datom($value); | ||
return this.freeze({$key, $value}); | ||
} | ||
//========================================================================================================= | ||
new_single_datom(name, ...P) { | ||
this.types.validate.datom_name(name); | ||
return this._new_datom(`^${name}`, ...P); | ||
//--------------------------------------------------------------------------------------------------------- | ||
_get_matcher(selector) { | ||
var R, re; | ||
if ((R = matcher_cache.get(selector)) != null) { | ||
/* TAINT might make this method part of API */ | ||
return R; | ||
} | ||
selector = selector.replace(/(?<!\\)\?/g, '.?'); | ||
selector = selector.replace(/(?<!\\)\*/g, '.*'); | ||
selector = `^(?:${selector})$`; | ||
re = new RegExp(selector, 'u'); | ||
R = function(x) { | ||
return re.test(x); | ||
}; | ||
matcher_cache.set(selector, R); | ||
return R; | ||
} | ||
new_open_datom(name, ...P) { | ||
this.types.validate.datom_name(name); | ||
return this.new_datom(`<${name}`, ...P); | ||
} | ||
new_close_datom(name, ...P) { | ||
this.types.validate.datom_name(name); | ||
return this.new_datom(`>${name}`, ...P); | ||
} | ||
new_system_datom(name, ...P) { | ||
this.types.validate.datom_name(name); | ||
return this.new_datom(`~${name}`, ...P); | ||
} | ||
new_text_datom(...P) { | ||
return this.new_single_datom('text', ...P); | ||
} | ||
new_end_datom() { | ||
return this.new_system_datom('end'); | ||
} | ||
new_warning(ref, message, d, ...other) { | ||
return this.new_system_datom('warning', d, {ref, message}, ...other); | ||
} | ||
new_xemitter(...P) { | ||
return new (require('./xemitter')).Xemitter(...P); | ||
} | ||
select(d, selector) { | ||
var g, k, match, ref1, ref2, ref3, ref4, stamped_values, v; | ||
if (selector == null) { | ||
select(d, ...selectors) { | ||
var i, len, ref, selector; | ||
if (!(selectors.length > 0)) { | ||
throw new Error("µ86606 expected a selector, got none"); | ||
@@ -270,44 +191,13 @@ } | ||
} | ||
//....................................................................................................... | ||
if ((match = (ref1 = selector.match(p2)) != null ? ref1 : selector.match(p1)) == null) { | ||
throw new Error(`µ37799 illegal selector ${rpr(selector)}`); | ||
if ((ref = d.$stamped) != null ? ref : false) { | ||
return false; | ||
} | ||
g = {}; | ||
ref2 = match.groups; | ||
for (k in ref2) { | ||
v = ref2[k]; | ||
if (v !== '') { | ||
g[k] = v; | ||
//....................................................................................................... | ||
for (i = 0, len = selectors.length; i < len; i++) { | ||
selector = selectors[i]; | ||
if ((this._get_matcher(selector))(d.$key)) { | ||
return true; | ||
} | ||
} | ||
if ((g.attribute != null) && (g.attribute !== 'stamped')) { | ||
throw new Error(`µ77764 unknown attribute name ${rpr(g.attribute)}`); | ||
} | ||
switch (g.value) { | ||
case void 0: | ||
stamped_values = [false]; | ||
break; | ||
case '*': | ||
stamped_values = [true, false]; | ||
break; | ||
case 'true': | ||
stamped_values = [true]; | ||
break; | ||
case 'false': | ||
stamped_values = [false]; | ||
break; | ||
default: | ||
throw new Error(`µ33366 illegal attribute or value in selector ${rpr(selector)}`); | ||
} | ||
if (ref3 = (ref4 = d.$stamped) != null ? ref4 : false, indexOf.call(stamped_values, ref3) < 0) { | ||
//....................................................................................................... | ||
return false; | ||
} | ||
if (g.key != null) { | ||
return d.$key === g.skey; | ||
} | ||
if (!d.$key.startsWith(g.sigil)) { | ||
return false; | ||
} | ||
return true; | ||
return false; | ||
} | ||
@@ -314,0 +204,0 @@ |
(function() { | ||
'use strict'; | ||
var GUY, Intertype, base_types, debug, echo, get_base_types, get_xemitter_types, inspect, log, misfit, rpr, xemitter_types; | ||
var GUY, Intertype, base_types, debug, echo, get_base_types, inspect, log, misfit, rpr; | ||
@@ -25,4 +25,2 @@ //########################################################################################################### | ||
xemitter_types = null; | ||
misfit = Symbol('misfit'); | ||
@@ -53,16 +51,6 @@ | ||
//......................................................................................................... | ||
/* TAINT ??? to be removed ??? */ declare.datom_sigil(function(x) { | ||
return x === '^' || x === '<' || x === '>' || x === '~' || x === '[' || x === ']'; | ||
/* TAINT ??? to be removed ??? */ declare.datom_key(function(x) { | ||
return (this.isa.text(x)) && (x.length > 0); | ||
}); | ||
//......................................................................................................... | ||
declare.datom_key(function(x) { | ||
if (!this.isa.text(x)) { | ||
return false; | ||
} | ||
if (!((Array.from(x)).length >= 2)) { | ||
return false; | ||
} | ||
return this.isa.datom_sigil(x[0]); | ||
}); | ||
//......................................................................................................... | ||
declare.datom_name('nonempty.text'); | ||
@@ -104,19 +92,4 @@ //......................................................................................................... | ||
//----------------------------------------------------------------------------------------------------------- | ||
get_xemitter_types = function() { | ||
var declare; | ||
if (xemitter_types != null) { | ||
return xemitter_types; | ||
} | ||
//......................................................................................................... | ||
xemitter_types = new Intertype(get_base_types()); | ||
({declare} = xemitter_types); | ||
//......................................................................................................... | ||
declare.callable('function'); | ||
//......................................................................................................... | ||
return xemitter_types; | ||
}; | ||
//=========================================================================================================== | ||
module.exports = {misfit, get_base_types, get_xemitter_types}; | ||
module.exports = {misfit, get_base_types}; | ||
@@ -123,0 +96,0 @@ }).call(this); |
{ | ||
"name": "datom", | ||
"version": "11.0.0", | ||
"version": "12.0.0", | ||
"description": "standardized immutable objects in the spirit of datomic, especially suited for use in data pipelines", | ||
@@ -26,4 +26,4 @@ "main": "lib/main.js", | ||
"dependencies": { | ||
"guy": "^12.7.0", | ||
"intertype": "0.111.0", | ||
"guy": "^13.5.0", | ||
"intertype": "0.114.0", | ||
"letsfreezethat": "^3.1.0" | ||
@@ -30,0 +30,0 @@ }, |
181
README.md
@@ -24,8 +24,2 @@ | ||
- [`select = ( d, selector ) ->`](#select---d-selector---) | ||
- [The XEmitter (XE) Sub-Module](#the-xemitter-xe-sub-module) | ||
- [XE Sending API](#xe-sending-api) | ||
- [XE Receiving API](#xe-receiving-api) | ||
- [Sample](#sample) | ||
- [Managing Scope](#managing-scope) | ||
- [Cup Of Datom](#cup-of-datom) | ||
- [Benchmarks](#benchmarks) | ||
@@ -68,3 +62,3 @@ - [To Do](#to-do) | ||
Or, mode idiomatically: | ||
Or, more idiomatically: | ||
@@ -329,173 +323,2 @@ ```coffee | ||
## The XEmitter (XE) Sub-Module | ||
### XE Sending API | ||
* **`XE.emit = ( key, d ) ->`** emit (a.k.a. 'publish', 'send to whom it may concern') an event. To | ||
be called either as `XE.emit '^mykey', 'myvalue'` or as `XE.emit PD.new_event '^mykey', 'myvalue'` (in | ||
which latter case the datom's key will become the channel key). When called with await as in | ||
`return_values = await XE.emit '^foo', ...`, `return_values` will be a list with all values returned by | ||
all listeners that got called for this event. | ||
* **`XE.delegate = ( key, d ) ->`** like `XE.emit()` but will pick out and unwrap the event value | ||
from the event contractor (see below). If no event contractor was listening, an error will be raised. | ||
### XE Receiving API | ||
* **`XE.listen_to_all = ( listener ) ->`** Register a listener for all events. | ||
* **`XE.listen_to_unheard = ( listener ) ->`** Register a listener for all events that do not have a | ||
listener or a contractor. | ||
* **`XE.listen_to = ( key, listener ) ->`** Register a listener for events that match `key`. No | ||
pattern matching is implemented atm, so you can only listen to all keys or a single key. | ||
* **`XE.contract = ( key, listener ) ->`** Register a contractor (a.k.a. 'result producer') for | ||
events that match `key`. | ||
<!-- The above methods—`XE.listen_to_all()`, `XE.listen_to()` and `XE.contract()`—will return an `unsubscribe()` | ||
function that, when called once, will unsubscribe the event listener from the event. | ||
--> | ||
### Sample | ||
```coffee | ||
PD = require 'pipedreams' | ||
defer = setImmediate | ||
XE = PD.XE.new_scope() | ||
#----------------------------------------------------------------------------------------------------------- | ||
### Register a 'contractor' (a.k.a. 'result producer') for `^plus-async` events; observe that asynchronous | ||
contractors should return a promise: ### | ||
XE.contract '^plus-async', ( d ) => | ||
return new Promise ( resolve, reject ) => | ||
defer => resolve d.value.a + d.value.b | ||
############################################################################################################ | ||
do => | ||
info 'µ28823-5', await XE.emit PD.new_event '^plus-async', { a: 42, b: 108, } | ||
# in case other listeners were registered that returned values like `'listener #1'` and so on, the | ||
# returned list of values might look like: | ||
# -> [ 'listener #4', { key: '~xemitter-preferred', value: 150 }, 'listener #1', 'listener #2' ] | ||
### When using `delegate()` instead of `emit()`, the preferred value (a.k.a. '*the* event result') | ||
will be picked out of the list and unwrapped for you: ### | ||
info 'µ28823-6', await XE.delegate PD.new_event '^plus-async', { a: 42, b: 108, } | ||
# -> 150 | ||
``` | ||
For a demo with more coverage, have a look at | ||
[experiments/demo-xemitter.coffee](https://github.com/loveencounterflow/pipedreams/blob/master/blob/master/src/experiments/demo-xemitter.coffee). | ||
### Managing Scope | ||
Typically, you'll start using XEmitter with `XE = PD.XE.new_scope()`; this creates a new 'scope' for events. | ||
Only methods that emit and listen to the same scope can exchange messages. When used within an application, | ||
you will want to publish that scope to all participating modules; one way to do so is to write a dedicated | ||
module with a single line in it, `module.exports = ( require 'pipedreams' ).XE.new_scope()`. | ||
# Cup Of Datom | ||
Class `Cupofdatom` is a derivative of [`Cupofjoe`](https://github.com/loveencounterflow/cupofjoe) that is | ||
geared towards easy declarative generation of nested sequences of datoms with a | ||
[teacup](https://github.com/goodeggs/teacup)-like syntax: | ||
```coffee | ||
c = new DATOM.Cupofdatom() | ||
c.cram 'helo', 'world' | ||
c.cram 'foo', -> | ||
c.cram 'bold', -> | ||
c.cram null, 'content' | ||
ds = c.expand() | ||
# `ds` is now a list of datoms: | ||
[ | ||
{ $key: '<helo' }, | ||
{ $key: '^text', text: 'world' }, | ||
{ $key: '>helo' }, | ||
{ $key: '<foo' }, | ||
{ $key: '<bold' }, | ||
{ $key: '^text', text: 'content' }, | ||
{ $key: '>bold' }, | ||
{ $key: '>foo' } ] | ||
``` | ||
* First argument to `cram()` becomes key of datom | ||
* therefore, must be a valid datom name | ||
* sigil will be `^` if called with no further arguments | ||
* or else two datoms with sigils `<` and `>` will be generated that surround their contents | ||
* text arguments will be turned into `^text` datoms | ||
* as with `Cupofjoe`, functions will be called, may either call `cram()` method or return value | ||
* return values will *not* be further analyzed but be kept as-is in the list returned by `expand()` | ||
* also possible to provide (in non-initial positions) objects whose members will become attributes of the | ||
respective datom: | ||
```coffee | ||
c = new DATOM.Cupofdatom { absorb: true, } # default value; Note: turn attributes off with { absorb: false, } | ||
c.cram 'greeting', 'helo', 'world' | ||
c.cram 'greeting', '早安', { lang: 'zh_CN', } | ||
c.cram 'greeting', { lang: 'zh_CN', 问候: '早安', time_of_day: 'morning', } | ||
c.cram 'text', { lang: 'hi', text: 'नमस्ते', } | ||
c.cram 'greeting', -> | ||
c.cram 'language', { $value: 'Japanese', } | ||
c.cram 'time_of_day', { $value: 'morning', } | ||
c.cram null, 'お早うございます' | ||
``` | ||
gives | ||
``` | ||
{ $key: '<greeting' } | ||
{ $key: '^text', text: 'helo', } | ||
{ $key: '^text', text: 'world', } | ||
{ $key: '>greeting' } | ||
{ $key: '<greeting', lang: 'zh_CN', } | ||
{ $key: '^text', text: '早安', } | ||
{ $key: '>greeting' } | ||
{ $key: '^greeting', lang: 'zh_CN', '问候': '早安', time_of_day: 'morning', } | ||
{ $key: '^text', text: 'नमस्ते', lang: 'hi', } | ||
{ $key: '<greeting' } | ||
{ $key: '^language', $value: 'Japanese', } | ||
{ $key: '^time_of_day', $value: 'morning', } | ||
{ $key: '^text', text: 'お早うございます', } | ||
{ $key: '>greeting' } | ||
``` | ||
Call patterns: | ||
* first argument is always: | ||
* the **basic name** (the `$key` of the datom minus the sigil) of the datom, | ||
* or else the **comprehensive name**, where implemented (for example in InterText `CupOfHtml`, this means | ||
one can give `div#c59.draggable.hilite` as first argument to produce elements with a tag name (`div`), | ||
an ID (`c59`), and HTML `class` attribute (`draggable hilite`) in one go | ||
* or else **`null`** to indicate absence of a specific name | ||
* when a name has been given | ||
* and there are content arguments, then a pair of `{ $key: '<name', }`, `{ $key: '>name', }` datoms | ||
will be produced, with the content arguments coming in between | ||
* in case no content has been given, a single `{ $key: '^name', }` datom will be produced | ||
* as for arguments in non-initial positions: | ||
* objects will be merged with `Object.assign()` and passed on to `DATOM.new_datom()`, so `cram 'foo', { | ||
id: 'c221', frob: true, x: 1, }, ... { x: 2, }` will produce `{ $key: '^foo', id: 'c221', frob: true, x: | ||
2, }`. | ||
* In case a key/value pair attributes argument conflicts with one set by an comprehensive name (as in | ||
`cram 'foo#IDA', { id: 'IDB', }`), the one in the attributes argument wins (as it would in a similar | ||
situation when using `Object.assign()`) | ||
* functions will be called without arguments | ||
* if a function itself calls `cram()` from the same instance, its return value will be discarded; | ||
* in case it does not call `cram()`, its return value will be discarded if it is `null` or `undefined`, | ||
and otherwise become a content argument *without being processed*, so contained functions will not be | ||
called and text values will not be wrapped in `{ $key: '^text', }` datoms | ||
* texts will be wrapped in `{ $key: '^text', 'text': ..., }` datoms | ||
* other values will be wrapped in `{ $key: '^value', '$value': ..., }` datoms, e.g. `cram null, 42, 'some | ||
text', true` will emit `{ $key: '^value', $value: 42, }, { $key: '^text', $text: 'some text', }, { $key: | ||
'^value', $value: true, }` | ||
```coffee | ||
cram name | ||
cram name, content1, content2, ... | ||
cram name, content1, ( -> function ), ... | ||
cram name, content1, ( -> cram ... ), ... | ||
cram name, { key: value, }, content1, ( -> cram ... ), ... | ||
``` | ||
# Benchmarks | ||
@@ -570,4 +393,6 @@ | ||
and cloning (`strcuturedClone()`, `Object.assign()`, `GUY.props.nonull_assign()`) | ||
* [ ] re-implement (syntax or method for) selecting stamped datoms | ||
* [ ] implement wildcards for `select()`; cache selectors to avoid re-interpretation of recurrent patterns | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
52855
8
260
395
1
+ Addedanymatch@3.1.3(transitive)
+ Addedbinary-extensions@2.3.0(transitive)
+ Addedbraces@3.0.3(transitive)
+ Addedchokidar@3.6.0(transitive)
+ Addedfill-range@7.1.1(transitive)
+ Addedfsevents@2.3.3(transitive)
+ Addedglob-parent@5.1.2(transitive)
+ Addedguy@13.7.2(transitive)
+ Addedintertype@0.114.0(transitive)
+ Addedis-binary-path@2.1.0(transitive)
+ Addedis-extglob@2.1.1(transitive)
+ Addedis-glob@4.0.3(transitive)
+ Addedis-number@7.0.0(transitive)
+ Addednormalize-path@3.0.0(transitive)
+ Addedpicomatch@2.3.1(transitive)
+ Addedreaddirp@3.6.0(transitive)
+ Addedto-regex-range@5.0.1(transitive)
- Removedguy@12.10.0(transitive)
- Removedintertype@0.111.0(transitive)
Updatedguy@^13.5.0
Updatedintertype@0.114.0