Skip to content

Commit

Permalink
Implement { map: true } in channels
Browse files Browse the repository at this point in the history
  • Loading branch information
martypdx committed Mar 14, 2024
1 parent bc21869 commit ad22f5a
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 66 deletions.
2 changes: 1 addition & 1 deletion packages/azoth/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "azoth",
"version": "1.1.15",
"version": "1.1.16",
"description": "Azoth JSX hypermedia",
"author": "Marty Nelson",
"license": "MIT",
Expand Down
38 changes: 24 additions & 14 deletions packages/channels/generators.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
import { ConflictingOptionsError, OptionMissingFunctionArgumentError } from './throw.js';

export function subject(transform, options) {
if(!options && typeof transform === 'object') {
options = transform;
transform = null;
}

let initialValue, startWith;
function resolveOptions(options, transform) {
let initialValue, startWith, map = false;
if(options) {
initialValue = options.initialValue;
startWith = options.startWith;
map = options.map ?? false;
if(initialValue !== undefined) {
if(startWith !== undefined) {
throw new Error('Cannot specify both initialValue and startWith option');
}
if(!transform) {
throw new Error('Cannot specify initialValue without a transform function');
}
if(startWith !== undefined) new ConflictingOptionsError();
if(!transform) throw new OptionMissingFunctionArgumentError('initialValue');
}
if(map && !transform) {
throw new OptionMissingFunctionArgumentError();
}
}
return { initialValue, startWith, map };
}

export function subject(transform, options) {
if(!options && typeof transform === 'object') {
options = transform;
transform = null;
}

const { initialValue, startWith, map } = resolveOptions(options, transform);

const relay = { resolve: null };

let unsentEarlyDispatch = null;

function dispatch(payload) {
if(transform) payload = transform(payload);
if(transform) {
if(map) payload = payload.map(transform);
else payload = transform(payload);
}

if(relay.resolve) relay.resolve(payload);
else {
// eslint-disable-next-line eqeqeq
Expand Down
8 changes: 4 additions & 4 deletions packages/channels/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@azothjs/channels",
"version": "0.1.3",
"version": "0.1.4",
"description": "Asynchronous layout channels",
"author": "Marty Nelson",
"license": "MIT",
Expand All @@ -21,12 +21,12 @@
"type": "module",
"main": "./index.js",
"files": [
"use.js",
"repeat.js",
"consume.js",
"throw.js",
"generators.js",
"index.js",
"repeat.js",
"throw.js",
"use.js",
"with-resolvers-polyfill.js",
"README"
],
Expand Down
18 changes: 17 additions & 1 deletion packages/channels/throw.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,20 @@ export class AsyncSourceTypeError extends TypeError {
Unexpected asynchronous data source type "${type}". Expected an async data provider type, or \
a function that returns an async data provider type."`);
}
}
}


export class OptionMissingFunctionArgumentError extends TypeError {
constructor(option = 'map: true') {
super(`\
More arguments needed: option "${option}" requires a mapping function.`);
}
}

export class ConflictingOptionsError extends TypeError {
constructor() {
super(`\
Cannot specify both initialValue and startWith options.`);
}
}

34 changes: 27 additions & 7 deletions packages/channels/use.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Multicast } from './generators.js';
import { AsyncSourceTypeError } from './throw.js';
import { AsyncSourceTypeError, OptionMissingFunctionArgumentError } from './throw.js';

export function use(asyncSource, ...args) {
const [channels, options] = getArguments(args);
Expand Down Expand Up @@ -33,21 +33,41 @@ function getArguments(channels) {

function fromPromise(promise, channel, options) {
const startWith = options?.startWith;
const map = options?.map ?? false;
if(startWith) {
return fromPromiseStartWith(promise, channel, startWith);
return fromPromiseStartWith(startWith, promise, channel, map);
}
return channel ? promise.then(channel) : promise;
return promiseResolution(promise, channel, map);
}

async function* fromPromiseStartWith(promise, channel, startWith) {
async function* fromPromiseStartWith(startWith, promise, channel, map) {
yield startWith;
yield channel ? promise.then(channel) : promise;
yield promiseResolution(promise, channel, map);
}

function promiseResolution(promise, channel, map) {
if(map) {
if(!channel) throw new OptionMissingFunctionArgumentError();
// TODO: include or suppress index? which param???
// collapse "slottable" back into props???
return promise.then(array => array.map(channel));
}
return channel ? promise.then(channel) : promise;
}

async function* fromAsyncIterator(iterator, channel, options) {
const startWith = options?.startWith;
const map = options?.map ?? false;
if(map && !channel) throw new OptionMissingFunctionArgumentError();

if(startWith) yield startWith;

for await(const value of iterator) {
if(map) {
yield value.map(channel);
continue;
}

yield channel ? channel(value) : value;
}
}
Expand All @@ -61,12 +81,12 @@ function branchPromise(promise, channels) {
});
}

function branchAsyncIterator(iterator, channels, options) {
function branchAsyncIterator(iterator, channels) {
const multicast = new Multicast(iterator);
return channels.map(channel => {
if(Array.isArray(channel)) { // [channel, options]
return multicast.subscriber(channel[0], channel[1]);
}
return multicast.subscriber(channel, options);
return multicast.subscriber(channel);
});
}
Loading

0 comments on commit ad22f5a

Please sign in to comment.