Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"@jsonjoy.com/util": "^1.9.0",
"sonic-forest": "^1.2.1",
"thingies": "^2.5.0",
"tree-dump": "^1.0.3"
"tree-dump": "^1.1.0"
},
"devDependencies": {
"@biomejs/biome": "^1.9.3",
Expand Down
14 changes: 9 additions & 5 deletions src/type/classes/FnType.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import {printTree} from 'tree-dump/lib/printTree';
import * as schema from '../../schema';
import type {SchemaOf, Type} from '../types';
import {AbsType} from './AbsType';

const fnNotImplemented: schema.FunctionValue<any, any> = async () => {
throw new Error('NOT_IMPLEMENTED');
};
import type {SchemaOf, Type} from '../types';

const toStringTree = (tab: string = '', type: FnType<Type, Type, any> | FnRxType<Type, Type, any>) => {
return printTree(tab, [
Expand Down Expand Up @@ -71,6 +67,14 @@ export class FnType<Req extends Type, Res extends Type, Ctx = unknown> extends A
return this;
}

public exec(input: schema.TypeOf<SchemaOf<Req>>) {
const func = this.schema.default as schema.FunctionValue<
schema.TypeOf<SchemaOf<Req>>,
schema.TypeOf<SchemaOf<Res>>
>;
return func(input);
}

public toString(tab: string = ''): string {
return super.toString(tab) + toStringTree(tab, this);
}
Expand Down
15 changes: 15 additions & 0 deletions src/value/FnValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Value} from './Value';
import type {Printable} from 'tree-dump/lib/types';
import type * as classes from '../type';

export class FnValue<T extends classes.FnType<any, any, any>> extends Value<T> implements Printable {
public async exec(input: classes.ResolveType<T['req']>, ctx?: unknown): Promise<Value<T['res']>> {
const fn = this.data as any;
const output = await fn(input, ctx);
return new Value(output, this.type!.res);
}

public name(): string {
return 'FnValue';
}
}
24 changes: 19 additions & 5 deletions src/value/ObjValue.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {printTree} from 'tree-dump/lib/printTree';
import {ModuleType} from '../type/classes/ModuleType';
import {Value} from './Value';
import {FnValue} from './FnValue';
import type {Printable} from 'tree-dump/lib/types';
import type * as classes from '../type';
import type {TypeBuilder} from '../type/TypeBuilder';
import {ModuleType} from '../type/classes/ModuleType';
import {Value} from './Value';

export type UnObjType<T> = T extends classes.ObjType<infer U> ? U : never;
export type UnObjValue<T> = T extends ObjValue<infer U> ? U : never;
Expand All @@ -14,6 +14,8 @@ export type ObjValueToTypeMap<F> = ToObject<{
[K in keyof F]: ObjFieldToTuple<F[K]>;
}>;

export type Ensure<T, X> = T extends X ? T : X;

export class ObjValue<T extends classes.ObjType<any>> extends Value<T> implements Printable {
public static new = (system: ModuleType = new ModuleType()) => new ObjValue({}, system.t.obj);

Expand Down Expand Up @@ -41,6 +43,18 @@ export class ObjValue<T extends classes.ObjType<any>> extends Value<T> implement
return new Value(data, field.val) as any;
}

public fn<K extends keyof ObjValueToTypeMap<UnObjType<T>>>(
key: K,
): FnValue<
Ensure<
ObjValueToTypeMap<UnObjType<T>>[K] extends classes.Type ? ObjValueToTypeMap<UnObjType<T>>[K] : classes.Type,
classes.FnType<any, any, any>
>
> {
const val = this.get(key);
return new FnValue(val.data, val.type as any);
}

public field<F extends classes.KeyType<any, any>>(
field: F | ((t: TypeBuilder) => F),
data: classes.ResolveType<UnObjFieldTypeVal<F>>,
Expand Down Expand Up @@ -78,7 +92,7 @@ export class ObjValue<T extends classes.ObjType<any>> extends Value<T> implement
return this as any;
}

public toString(tab: string = ''): string {
return 'ObjValue' + printTree(tab, [(tab) => this.type!.toString(tab)]);
public name(): string {
return 'ObjValue';
}
}
26 changes: 25 additions & 1 deletion src/value/Value.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,40 @@
import {printTree} from 'tree-dump/lib/printTree';
import {printJson} from 'tree-dump/lib/printJson';
import type {Printable} from 'tree-dump';
import type {ResolveType, Type} from '../type/types';

const copyForPrint = (data: unknown): unknown => {
if (typeof data === 'function') return '__fN---';
if (Array.isArray(data)) return data.map(copyForPrint);
if (data && typeof data === 'object') {
const res: Record<string, unknown> = {};
for (const k in data) res[k] = copyForPrint((data as any)[k]);
return res;
}
return data;
};

export class Value<T extends Type = Type> implements Printable {
constructor(
public data: ResolveType<T>,
public type?: T,
) {}

public name(): string {
return 'Value';
}

public toString(tab: string = ''): string {
const type = this.type;
return 'Value' + (type ? printTree(tab, [(tab) => type.toString(tab)]) : '');
return (
this.name() +
(type
? printTree(tab, [
(tab) => type.toString(tab),
(tab) => printJson(tab, copyForPrint(this.data)).replace(/"__fN---"/g, 'fn()'),
])
: '')
);
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/value/__tests__/ObjValue-router.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {createRouter} from './ObjValue.fixtures';

test('can retrieve field as Value', async () => {
const log = jest.fn();
const router = createRouter({log});
const result = await router.fn('log.message').exec({message: 'asdf'});
expect(result.data).toEqual({time: expect.any(Number)});
});
41 changes: 41 additions & 0 deletions src/value/__tests__/ObjValue.fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type {ObjType} from '../../type';
import type {TypeBuilder} from '../../type/TypeBuilder';
import {ObjValue} from '../ObjValue';

interface Services {
log: (msg: string) => void;
}

export interface RouteDeps {
svc: Services;
t: TypeBuilder;
}
export type RouterBase = ObjType<any>;
export type Router<R extends RouterBase> = ObjValue<R>;

const addLogMessageRoute =
({t, svc}: RouteDeps) =>
<R extends RouterBase>(r: Router<R>) => {
return r.set(
'log.message',
t.fn
.inp(t.Object(t.Key('message', t.str)))
.out(
t.object({
time: t.num,
}),
)
.value(({message}) => {
svc.log(message);
return {time: Date.now()};
}),
);
};

export const createRouter = (svc: Services) => {
const base = ObjValue.new();
const t = base.system.t;
const deps: RouteDeps = {svc, t};
const router = addLogMessageRoute(deps)(base);
return router;
};
9 changes: 6 additions & 3 deletions src/value/__tests__/__snapshots__/ObjValue.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

exports[`can print to string 1`] = `
"ObjValue
└─ obj
└─ "foo"
└─ str"
├─ obj
│ └─ "foo"
│ └─ str
└─ {
"foo": "bar"
}"
`;
13 changes: 11 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,7 @@ __metadata:
rxjs: "npm:^7.8.2"
sonic-forest: "npm:^1.2.1"
thingies: "npm:^2.5.0"
tree-dump: "npm:^1.0.3"
tree-dump: "npm:^1.1.0"
ts-jest: "npm:^29.1.2"
tslib: "npm:^2.7.0"
typescript: "npm:^5.6.2"
Expand Down Expand Up @@ -3530,7 +3530,7 @@ __metadata:
languageName: node
linkType: hard

"tree-dump@npm:^1.0.0, tree-dump@npm:^1.0.3":
"tree-dump@npm:^1.0.0":
version: 1.0.3
resolution: "tree-dump@npm:1.0.3"
peerDependencies:
Expand All @@ -3539,6 +3539,15 @@ __metadata:
languageName: node
linkType: hard

"tree-dump@npm:^1.1.0":
version: 1.1.0
resolution: "tree-dump@npm:1.1.0"
peerDependencies:
tslib: 2
checksum: 10c0/079f0f0163b68ee2eedc65cab1de6fb121487eba9ae135c106a8bc5e4ab7906ae0b57d86016e4a7da8c0ee906da1eae8c6a1490cd6e2a5e5ccbca321e1f959ca
languageName: node
linkType: hard

"ts-jest@npm:^29.1.2":
version: 29.4.1
resolution: "ts-jest@npm:29.4.1"
Expand Down