Skip to content

Commit

Permalink
Merge pull request #83 from Jujulego/feat/builders
Browse files Browse the repository at this point in the history
Rework builders
  • Loading branch information
Jujulego authored Jul 9, 2022
2 parents 410522b + 6dd90a0 commit d583c87
Show file tree
Hide file tree
Showing 26 changed files with 778 additions and 553 deletions.
12 changes: 12 additions & 0 deletions .idea/runConfigurations/build__aegis_.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/runConfigurations/watch__aegis_.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions packages/aegis/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jujulego/aegis",
"version": "1.0.0-alpha.1",
"version": "1.0.0-alpha.2",
"license": "MIT",
"author": "Julien Capellari <julien.capellari@google.com>",
"repository": {
Expand Down Expand Up @@ -60,5 +60,6 @@
"regenerator-runtime": "0.13.9",
"ts-node": "10.8.2",
"typescript": "4.7.4"
}
},
"stableVersion": "1.0.0-alpha.1"
}
137 changes: 0 additions & 137 deletions packages/aegis/src/entity.builder.ts

This file was deleted.

61 changes: 61 additions & 0 deletions packages/aegis/src/entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Entity, Query, RefreshStrategy, Store } from '@jujulego/aegis-core';

import { $item, AegisItem, AegisUnknownItem } from './item';
import { $list, AegisList } from './list';
import { AegisId, AegisIdExtractor, AegisProtocol, Fetcher, Refreshable } from './utils';

// Types
export type AegisEntity<T, I extends AegisId, P extends AegisProtocol> = P & {
readonly $entity: Entity<T>;

$item(id: I): AegisItem<T, I>;
$queryItem<N extends string, A extends unknown[]>(name: N, fetcher: Fetcher<A, Query<T>>): AegisEntity<T, I, P & Record<N, Fetcher<A, AegisUnknownItem<T, I>>>>;
$queryItem<N extends string, A extends unknown[]>(name: N, fetcher: Fetcher<A, Query<T>>, id: AegisIdExtractor<A, I>, strategy?: RefreshStrategy): AegisEntity<T, I, P & Record<N, Fetcher<A, AegisItem<T, I> & Refreshable<T>>>>;

$list(key: string): AegisList<T>;
$queryList<N extends string, A extends unknown[]>(name: N, fetcher: Fetcher<A, Query<T[]>>, strategy?: RefreshStrategy): AegisEntity<T, I, P & Record<N, Fetcher<[string, ...A], AegisList<T>>>>;
}

// Entity builder
// eslint-disable-next-line @typescript-eslint/ban-types
export function $entity<T, I extends AegisId>(name: string, store: Store, extractor: (itm: T) => I): AegisEntity<T, I, {}> {
const entity = new Entity<T>(name, store, (itm) => JSON.stringify(extractor(itm)));

return {
$entity: entity,

$item(id: I): AegisItem<T, I> {
return $item(entity, id);
},

$queryItem<N extends string, A extends unknown[]>(name: N, fetcher: Fetcher<A, Query<T>>, id?: AegisIdExtractor<A, I>, strategy: RefreshStrategy = 'keep') {
this[name] = (...args: A) => {
if (!id) {
return $item<T, I>(entity, fetcher(...args));
} else {
const item = $item(entity, id(...args), () => fetcher(...args));
item.$item.refresh(() => fetcher(...args), strategy);

return item;
}
};

return this;
},

$list(key: string): AegisList<T> {
return $list(entity, key);
},

$queryList<N extends string, A extends unknown[]>(name: N, fetcher: Fetcher<A, Query<T[]>>, strategy: RefreshStrategy = 'keep') {
this[name] = (key: string, ...args: A) => {
const list = $list(entity, key, () => fetcher(...args));
list.$list.refresh(() => fetcher(...args), strategy);

return list;
};

return this;
}
};
}
7 changes: 5 additions & 2 deletions packages/aegis/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export * from '@jujulego/aegis-core';

export * from './entity.builder';
export * from './store.builder';
export * from './entity';
export * from './item';
export * from './list';
export * from './store';
export * from './utils';
128 changes: 128 additions & 0 deletions packages/aegis/src/item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import {
Entity,
EventListener,
EventListenerOptions, EventSource, EventType,
EventUnsubscribe, ExtractKey, Item,
PartialKey, Query, QueryManagerEventMap, RefreshStrategy,
StoreEventMap
} from '@jujulego/aegis-core';

import { AegisId, Refreshable } from './utils';

// Types
export interface AegisUnknownItem<T, I extends AegisId = AegisId> {
readonly $id?: I;
readonly $item?: Item<T>;
readonly $entity: Entity<T>;

readonly isLoading: boolean;
data?: T;

subscribe(
key: 'update',
listener: EventListener<StoreEventMap<T>, `update.${string}.${string}`>,
opts?: EventListenerOptions
): EventUnsubscribe;
subscribe<T extends PartialKey<EventType<QueryManagerEventMap<T>>>>(
type: T,
listener: EventListener<QueryManagerEventMap<T>, ExtractKey<EventType<QueryManagerEventMap<T>>, T>>,
opts?: EventListenerOptions
): EventUnsubscribe;
}

export interface AegisItem<T, I extends AegisId = AegisId> extends AegisUnknownItem<T, I> {
readonly $id: I;
readonly $item: Item<T>;
}

// Item builder
export function $item<T, I extends AegisId>(entity: Entity<T>, id: I): AegisItem<T, I>;
export function $item<T, I extends AegisId>(entity: Entity<T>, id: I, refresh: () => Query<T>): AegisItem<T, I> & Refreshable<T>;
export function $item<T, I extends AegisId>(entity: Entity<T>, query: Query<T>): AegisUnknownItem<T, I>;

export function $item<T, I extends AegisId>(entity: Entity<T>, arg1: I | Query<T>, refresh?: () => Query<T>) {
if (arg1 instanceof Query) {
const events = new EventSource<StoreEventMap<T> & QueryManagerEventMap<T>>();

const item: AegisUnknownItem<T, I> = {
$entity: entity,

get subscribe() {
return events.subscribe.bind(events);
},

get isLoading() {
return this.$item?.isLoading ?? (arg1.status === 'pending');
},

get data() {
return this.$item?.data;
},
set data(value: T | undefined) {
if (!this.$item) {
throw new Error('Cannot update unknown item');
}

this.$item.data = value;
}
};

// Events
arg1.subscribe('update.failed', (state, mtd) => {
events.emit('query.failed', state, { source: mtd.source });
});

arg1.subscribe('update.completed', ({ result }, mtd) => {
const id = entity.storeItem(result);
const $item = entity.item(id);

Object.assign(item, {
$id: JSON.parse(id),
$item
});

$item.subscribe('update', (data, mtd) => {
events.emit(mtd.type, data, { source: mtd.source });
});

$item.subscribe('query', (data, mtd) => {
events.emit(mtd.type, data, { source: mtd.source });
});

events.emit('query.completed', { status: 'completed', result: result }, { source: mtd.source });
});

return item;
} else {
const item: AegisItem<T, I> = {
$id: arg1,
$item: entity.item(JSON.stringify(arg1)),
$entity: entity,

subscribe(...args: unknown[]) {
return this.$item.subscribe(...args);
},

get isLoading() {
return this.$item.isLoading;
},

get data() {
return this.$item.data;
},
set data(value: T | undefined) {
this.$item.data = value;
}
};

if (refresh) {
return Object.assign(item, {
refresh(strategy: RefreshStrategy = 'keep') {
return item.$item.refresh(refresh, strategy);
}
});
}

return item;
}
}
Loading

0 comments on commit d583c87

Please sign in to comment.