@gi-tcg/gts-runtime 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +88 -78
- package/dist/index.js +63 -37
- package/package.json +5 -2
- package/src/attribute_return.ts +34 -0
- package/src/index.ts +12 -3
- package/src/symbols.ts +4 -18
- package/src/view.ts +3 -3
- package/src/view_model.ts +164 -91
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,40 @@
|
|
|
1
|
-
declare
|
|
2
|
-
type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
type
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
1
|
+
declare namespace AttributeReturn {
|
|
2
|
+
type This<TMeta> = {
|
|
3
|
+
"~meta": TMeta;
|
|
4
|
+
};
|
|
5
|
+
type EnableIf<
|
|
6
|
+
Condition,
|
|
7
|
+
T = void
|
|
8
|
+
> = Condition extends true ? T : never;
|
|
9
|
+
type Done = {
|
|
10
|
+
namedDefinition: {
|
|
11
|
+
"~meta": void;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
type With<
|
|
15
|
+
VM extends IViewModel<any, any>,
|
|
16
|
+
TMeta = VM["~namedDefinition"]["~meta"]
|
|
17
|
+
> = {
|
|
18
|
+
namedDefinition: BlockDefinitionRewriteMeta<VM["~namedDefinition"], TMeta>;
|
|
19
|
+
};
|
|
20
|
+
type DoneRewriteMeta<NewMeta> = {
|
|
21
|
+
namedDefinition: {
|
|
22
|
+
"~meta": void;
|
|
23
|
+
};
|
|
24
|
+
rewriteMeta: NewMeta;
|
|
25
|
+
};
|
|
26
|
+
type WithRewriteMeta<
|
|
27
|
+
VM extends IViewModel<any, any>,
|
|
28
|
+
NewMeta
|
|
29
|
+
> = {
|
|
30
|
+
namedDefinition: VM["~namedDefinition"];
|
|
31
|
+
rewriteMeta: NewMeta;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
type Meta = "~meta";
|
|
35
|
+
type Action = "~action";
|
|
36
|
+
type NamedDefinition = "~namedDefinition";
|
|
37
|
+
type Prelude = "~prelude";
|
|
14
38
|
type AttributeName = string | symbol;
|
|
15
39
|
interface SingleAttributeNode {
|
|
16
40
|
name: AttributeName;
|
|
@@ -34,109 +58,95 @@ declare class BindingContext {
|
|
|
34
58
|
addBinding(value: unknown): void;
|
|
35
59
|
getBindings(): unknown[];
|
|
36
60
|
}
|
|
37
|
-
declare function createDefine(rootVM:
|
|
38
|
-
declare function createBinding(rootVM:
|
|
61
|
+
declare function createDefine(rootVM: IViewModel<any, any>, node: SingleAttributeNode): void;
|
|
62
|
+
declare function createBinding(rootVM: IViewModel<any, any>, node: SingleAttributeNode): unknown[];
|
|
63
|
+
import { StandardJSONSchemaV1 } from "@standard-schema/spec";
|
|
39
64
|
interface AttributeBlockDefinition {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
[Meta]: any;
|
|
65
|
+
"~action"?: AttributeDefinition | undefined;
|
|
66
|
+
"~meta": any;
|
|
43
67
|
}
|
|
44
68
|
type Computed<T> = T extends infer U extends { [K in keyof T] : unknown } ? U : never;
|
|
45
69
|
type BlockDefinitionRewriteMeta<
|
|
46
70
|
BlockDef extends AttributeBlockDefinition,
|
|
47
71
|
NewMeta
|
|
48
72
|
> = Computed<Omit<BlockDef, Meta> & {
|
|
49
|
-
|
|
73
|
+
"~meta": NewMeta;
|
|
50
74
|
}> extends infer R extends AttributeBlockDefinition ? R : never;
|
|
51
75
|
interface IViewModel<
|
|
52
76
|
ModelT,
|
|
53
|
-
BlockDef extends AttributeBlockDefinition
|
|
54
|
-
|
|
55
|
-
parse(view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>): ModelT;
|
|
56
|
-
}
|
|
57
|
-
interface ViewModel<
|
|
58
|
-
ModelT,
|
|
59
|
-
BlockDef extends AttributeBlockDefinition
|
|
77
|
+
BlockDef extends AttributeBlockDefinition,
|
|
78
|
+
CtorArgs extends any[]
|
|
60
79
|
> {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
* @internal
|
|
64
|
-
*/
|
|
65
|
-
_symbols: AllSymbols;
|
|
66
|
-
[NamedDefinition]: BlockDef;
|
|
80
|
+
parse(view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>, ...args: CtorArgs): ModelT;
|
|
81
|
+
"~namedDefinition": BlockDef;
|
|
67
82
|
}
|
|
83
|
+
type LazyAttributeActionOrBinder<ModelT> = (model: ModelT, positionals: () => unknown[], named: View<any>) => unknown;
|
|
68
84
|
declare class ViewModel<
|
|
69
85
|
ModelT,
|
|
70
|
-
BlockDef extends AttributeBlockDefinition
|
|
71
|
-
|
|
86
|
+
BlockDef extends AttributeBlockDefinition,
|
|
87
|
+
CtorArgs extends any[]
|
|
88
|
+
> implements IViewModel<ModelT, BlockDef, CtorArgs> {
|
|
72
89
|
private;
|
|
73
|
-
|
|
74
|
-
constructor(Ctor: new () => ModelT);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
parse(view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>): ModelT;
|
|
90
|
+
"~namedDefinition": BlockDef;
|
|
91
|
+
constructor(Ctor: new (...args: CtorArgs) => ModelT);
|
|
92
|
+
"~setActionOrBinder"(context: "action" | "binder", name: PropertyKey, action: LazyAttributeActionOrBinder<ModelT>): void;
|
|
93
|
+
parse(view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>, ...args: CtorArgs): ModelT;
|
|
78
94
|
}
|
|
79
95
|
declare class AttributeDefHelper<ModelT> {
|
|
80
96
|
private;
|
|
81
|
-
constructor(viewModel: ViewModel<ModelT, any>);
|
|
82
|
-
|
|
97
|
+
constructor(viewModel: ViewModel<ModelT, any, any>);
|
|
98
|
+
"~assignActions"(defResult: Partial<Record<string, unknown>>): void;
|
|
83
99
|
attribute<T extends AttributeDefinition & {
|
|
84
100
|
as?: undefined;
|
|
85
|
-
}>(action: AttributeAction<ModelT, T>, binder?: AttributeBinder<ModelT, T> |
|
|
101
|
+
}>(action: AttributeAction<ModelT, T>, binder?: AttributeBinder<ModelT, T> | IViewModel<any, ReturnType<T>["namedDefinition"], []>): T;
|
|
86
102
|
attribute<T extends AttributeDefinition>(action: AttributeAction<ModelT, T>, binder: AttributeBinder<ModelT, T>): T;
|
|
103
|
+
simpleAttribute<Args extends any[]>(action: (this: ModelT, ...args: Args) => void): {
|
|
104
|
+
(...args: Args): AttributeReturn.Done;
|
|
105
|
+
};
|
|
106
|
+
simpleAttribute<
|
|
107
|
+
Args extends any[],
|
|
108
|
+
U
|
|
109
|
+
>(action: (this: ModelT, ...args: Args) => void, binder: (this: ModelT, ...args: Args) => U): {
|
|
110
|
+
(...args: Args): AttributeReturn.Done;
|
|
111
|
+
as?(): U;
|
|
112
|
+
};
|
|
87
113
|
}
|
|
88
114
|
declare function defineViewModel<
|
|
89
115
|
T,
|
|
90
|
-
const BlockDef extends Record<string, AttributeDefinition
|
|
116
|
+
const BlockDef extends Partial<Record<string | Action, AttributeDefinition>>,
|
|
117
|
+
CtorArgs extends any[] = [],
|
|
91
118
|
InitMeta = void
|
|
92
|
-
>(Ctor: new () => T, modelDefFn: (helper: AttributeDefHelper<T>) => BlockDef, initMeta?: InitMeta):
|
|
93
|
-
|
|
94
|
-
}>;
|
|
119
|
+
>(Ctor: new (...args: CtorArgs) => T, modelDefFn: (helper: AttributeDefHelper<T>) => BlockDef, initMeta?: InitMeta): IViewModel<T, BlockDef & {
|
|
120
|
+
"~meta": InitMeta;
|
|
121
|
+
}, CtorArgs>;
|
|
122
|
+
type SimpleViewModel<T> = IViewModel<T, { [K in keyof T]-? : {
|
|
123
|
+
(value: T[K]): AttributeReturn.Done;
|
|
124
|
+
uniqueKey(): K;
|
|
125
|
+
required(): {} extends Pick<T, K> ? false : true;
|
|
126
|
+
} } & {
|
|
127
|
+
"~meta": undefined;
|
|
128
|
+
}, []>;
|
|
129
|
+
declare function defineSimpleViewModel<const T extends StandardJSONSchemaV1>(schema: T): SimpleViewModel<StandardJSONSchemaV1.InferInput<T>>;
|
|
95
130
|
interface AttributeDefinition {
|
|
96
131
|
(...args: any[]): AttributePositionalReturnBase;
|
|
97
132
|
as?(): any;
|
|
133
|
+
required?(): boolean;
|
|
134
|
+
uniqueKey?(): string;
|
|
98
135
|
}
|
|
99
136
|
interface AttributePositionalReturnBase {
|
|
100
137
|
rewriteMeta?: any;
|
|
101
138
|
namedDefinition: AttributeBlockDefinition;
|
|
102
139
|
}
|
|
103
|
-
declare namespace AttributeReturn {
|
|
104
|
-
type This<TMeta = any> = {
|
|
105
|
-
[Meta]: TMeta;
|
|
106
|
-
};
|
|
107
|
-
type EnableIf<
|
|
108
|
-
Condition,
|
|
109
|
-
T = void
|
|
110
|
-
> = Condition extends true ? T : never;
|
|
111
|
-
type Done = {
|
|
112
|
-
namedDefinition: {
|
|
113
|
-
[Meta]: void;
|
|
114
|
-
};
|
|
115
|
-
};
|
|
116
|
-
type With<
|
|
117
|
-
VM extends ViewModel<any, any>,
|
|
118
|
-
TMeta = VM[NamedDefinition][Meta]
|
|
119
|
-
> = {
|
|
120
|
-
namedDefinition: BlockDefinitionRewriteMeta<VM[NamedDefinition], TMeta>;
|
|
121
|
-
};
|
|
122
|
-
type WithRewriteMeta<
|
|
123
|
-
VM extends ViewModel<any, any>,
|
|
124
|
-
Meta
|
|
125
|
-
> = {
|
|
126
|
-
namedDefinition: VM[NamedDefinition];
|
|
127
|
-
rewriteMeta: Meta;
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
140
|
type AttributeAction<
|
|
131
141
|
Model,
|
|
132
142
|
T extends AttributeDefinition
|
|
133
|
-
> = (model: Model, positional:
|
|
134
|
-
|
|
143
|
+
> = (model: Model, positional: Parameters<T>, named: View<ReturnType<T>["namedDefinition"] extends AttributeBlockDefinition ? ReturnType<T>["namedDefinition"] : {
|
|
144
|
+
"~meta": void;
|
|
135
145
|
}>) => void;
|
|
136
146
|
type AttributeBinder<
|
|
137
147
|
Model,
|
|
138
148
|
T extends AttributeDefinition
|
|
139
|
-
> = (model: Model, positional:
|
|
140
|
-
|
|
149
|
+
> = (model: Model, positional: Parameters<T>, named: View<ReturnType<T>["namedDefinition"] extends AttributeBlockDefinition ? ReturnType<T>["namedDefinition"] : {
|
|
150
|
+
"~meta": void;
|
|
141
151
|
}>) => T["as"] extends () => infer U ? U : void;
|
|
142
|
-
export { defineViewModel, createDefine, createBinding, Prelude, IViewModel, AttributeReturn, Action };
|
|
152
|
+
export { defineViewModel, defineSimpleViewModel, createDefine, createBinding, View, SingleAttributeNode, SimpleViewModel, Prelude, NamedDefinition, NamedAttributesNode, Meta, IViewModel, AttributeReturn, AttributeDefinition, Action, AttributeReturn as AR };
|
package/dist/index.js
CHANGED
|
@@ -31,26 +31,28 @@ function createBinding(rootVM, node) {
|
|
|
31
31
|
|
|
32
32
|
// packages/runtime/src/view_model.ts
|
|
33
33
|
class ViewModel {
|
|
34
|
-
Ctor;
|
|
35
34
|
#registeredActions = new Map;
|
|
36
35
|
#registeredBinders = new Map;
|
|
36
|
+
#Ctor;
|
|
37
37
|
constructor(Ctor) {
|
|
38
|
-
this
|
|
38
|
+
this.#Ctor = Ctor;
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
"~setActionOrBinder"(context, name, action) {
|
|
41
|
+
if (context === "action") {
|
|
42
|
+
this.#registeredActions.set(name, action);
|
|
43
|
+
} else {
|
|
44
|
+
this.#registeredBinders.set(name, action);
|
|
45
|
+
}
|
|
45
46
|
}
|
|
46
|
-
parse(view) {
|
|
47
|
-
const model = new this
|
|
47
|
+
parse(view, ...args) {
|
|
48
|
+
const model = new this.#Ctor(...args);
|
|
48
49
|
for (const attrNode of view._node.attributes) {
|
|
49
50
|
let { name, positionals, named, binding } = attrNode;
|
|
50
51
|
const insideBindingCtx = !!view._bindingCtx;
|
|
51
52
|
let fn = (insideBindingCtx ? this.#registeredBinders : this.#registeredActions).get(name);
|
|
52
53
|
if (!insideBindingCtx && !fn) {
|
|
53
|
-
|
|
54
|
+
const modelName = this.#Ctor.name;
|
|
55
|
+
throw new Error(`No action registered for attribute "${String(name)}" on model "${modelName}"`);
|
|
54
56
|
}
|
|
55
57
|
fn ??= () => {};
|
|
56
58
|
named ??= { attributes: [] };
|
|
@@ -68,56 +70,80 @@ class AttributeDefHelper {
|
|
|
68
70
|
constructor(viewModel) {
|
|
69
71
|
this.#viewModel = viewModel;
|
|
70
72
|
}
|
|
71
|
-
static #
|
|
72
|
-
static #
|
|
73
|
-
|
|
74
|
-
for (const [name,
|
|
75
|
-
const actionDescriptor = Object.getOwnPropertyDescriptor(
|
|
73
|
+
static #lazyActionSlot = Symbol("actionSlot");
|
|
74
|
+
static #lazyBinderSlot = Symbol("binderSlot");
|
|
75
|
+
"~assignActions"(defResult) {
|
|
76
|
+
for (const [name, value] of Object.entries(defResult)) {
|
|
77
|
+
const actionDescriptor = Object.getOwnPropertyDescriptor(value, AttributeDefHelper.#lazyActionSlot);
|
|
76
78
|
if (actionDescriptor) {
|
|
77
|
-
this.#viewModel
|
|
79
|
+
this.#viewModel["~setActionOrBinder"]("action", name, actionDescriptor.value);
|
|
78
80
|
}
|
|
79
|
-
const binderDescriptor = Object.getOwnPropertyDescriptor(
|
|
81
|
+
const binderDescriptor = Object.getOwnPropertyDescriptor(value, AttributeDefHelper.#lazyBinderSlot);
|
|
80
82
|
if (binderDescriptor) {
|
|
81
|
-
this.#viewModel
|
|
83
|
+
this.#viewModel["~setActionOrBinder"]("binder", name, binderDescriptor.value);
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
86
|
}
|
|
85
87
|
attribute(action, binder) {
|
|
86
|
-
|
|
88
|
+
const returnValue = {};
|
|
89
|
+
const lazyAction = (model, positionals, named) => action(model, positionals(), named);
|
|
90
|
+
Object.defineProperty(returnValue, AttributeDefHelper.#lazyActionSlot, {
|
|
91
|
+
value: lazyAction,
|
|
92
|
+
enumerable: true
|
|
93
|
+
});
|
|
94
|
+
let lazyBinder;
|
|
95
|
+
if (typeof binder === "function") {
|
|
96
|
+
lazyBinder = (model, positionals, named) => binder(model, positionals(), named);
|
|
97
|
+
} else if (binder) {
|
|
87
98
|
const vm = binder;
|
|
88
|
-
|
|
99
|
+
lazyBinder = (model, positionals, named) => {
|
|
89
100
|
return vm.parse(named);
|
|
90
101
|
};
|
|
102
|
+
} else {
|
|
103
|
+
lazyBinder = () => {};
|
|
91
104
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
Object.defineProperty(returnValue, AttributeDefHelper.#actionSlot, {
|
|
95
|
-
value: action,
|
|
96
|
-
enumerable: true
|
|
97
|
-
});
|
|
98
|
-
Object.defineProperty(returnValue, AttributeDefHelper.#binderSlot, {
|
|
99
|
-
value: binder,
|
|
105
|
+
Object.defineProperty(returnValue, AttributeDefHelper.#lazyBinderSlot, {
|
|
106
|
+
value: lazyBinder,
|
|
100
107
|
enumerable: true
|
|
101
108
|
});
|
|
102
109
|
return returnValue;
|
|
103
110
|
}
|
|
111
|
+
simpleAttribute(action, binder) {
|
|
112
|
+
const action2 = (model, positionals) => action.apply(model, positionals);
|
|
113
|
+
let binder2;
|
|
114
|
+
if (binder) {
|
|
115
|
+
binder2 = (model, positionals) => binder.apply(model, positionals);
|
|
116
|
+
}
|
|
117
|
+
return this.attribute(action2, binder2);
|
|
118
|
+
}
|
|
104
119
|
}
|
|
105
120
|
function defineViewModel(Ctor, modelDefFn, initMeta) {
|
|
106
121
|
const vm = new ViewModel(Ctor);
|
|
107
122
|
const helper = new AttributeDefHelper(vm);
|
|
108
123
|
const defResult = modelDefFn(helper);
|
|
109
|
-
helper
|
|
124
|
+
helper["~assignActions"](defResult);
|
|
125
|
+
return vm;
|
|
126
|
+
}
|
|
127
|
+
function defineSimpleViewModel(schema) {
|
|
128
|
+
const jsonSchema = schema["~standard"].jsonSchema.input({
|
|
129
|
+
target: "draft-2020-12"
|
|
130
|
+
});
|
|
131
|
+
const Ctor = class SimpleViewModel {
|
|
132
|
+
};
|
|
133
|
+
const vm = new ViewModel(Ctor);
|
|
134
|
+
const helper = new AttributeDefHelper(vm);
|
|
135
|
+
const defResult = {};
|
|
136
|
+
for (const key of Object.keys(jsonSchema.properties ?? {})) {
|
|
137
|
+
defResult[key] = helper.simpleAttribute(function(value) {
|
|
138
|
+
this[key] = value;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
helper["~assignActions"](defResult);
|
|
110
142
|
return vm;
|
|
111
143
|
}
|
|
112
|
-
// packages/runtime/src/symbols.ts
|
|
113
|
-
var Meta = Symbol("Meta");
|
|
114
|
-
var Action = Symbol("Action");
|
|
115
|
-
var NamedDefinition = Symbol("NamedDefinition");
|
|
116
|
-
var Prelude = Symbol("Prelude");
|
|
117
144
|
export {
|
|
118
145
|
defineViewModel,
|
|
146
|
+
defineSimpleViewModel,
|
|
119
147
|
createDefine,
|
|
120
|
-
createBinding
|
|
121
|
-
Prelude,
|
|
122
|
-
Action
|
|
148
|
+
createBinding
|
|
123
149
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gi-tcg/gts-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"repository": "https://github.com/piovium/gts.git",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -16,5 +16,8 @@
|
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {},
|
|
19
|
-
"devDependencies": {
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@standard-schema/spec": "^1.1.0",
|
|
21
|
+
"json-schema-typed": "^8.0.2"
|
|
22
|
+
}
|
|
20
23
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { BlockDefinitionRewriteMeta, IViewModel } from "./view_model";
|
|
2
|
+
|
|
3
|
+
export namespace AttributeReturn {
|
|
4
|
+
export type This<TMeta> = {
|
|
5
|
+
"~meta": TMeta;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type EnableIf<Condition, T = void> = Condition extends true
|
|
9
|
+
? T
|
|
10
|
+
: never;
|
|
11
|
+
|
|
12
|
+
export type Done = {
|
|
13
|
+
namedDefinition: { "~meta": void };
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type With<
|
|
17
|
+
VM extends IViewModel<any, any>,
|
|
18
|
+
TMeta = VM["~namedDefinition"]["~meta"],
|
|
19
|
+
> = {
|
|
20
|
+
namedDefinition: BlockDefinitionRewriteMeta<VM["~namedDefinition"], TMeta>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type DoneRewriteMeta<NewMeta> = {
|
|
24
|
+
namedDefinition: { "~meta": void };
|
|
25
|
+
rewriteMeta: NewMeta;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type WithRewriteMeta<VM extends IViewModel<any, any>, NewMeta> = {
|
|
29
|
+
namedDefinition: VM["~namedDefinition"];
|
|
30
|
+
rewriteMeta: NewMeta;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type { AttributeReturn as AR };
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
export {
|
|
2
2
|
defineViewModel,
|
|
3
|
+
defineSimpleViewModel,
|
|
4
|
+
type AttributeDefinition,
|
|
3
5
|
type IViewModel,
|
|
4
|
-
type
|
|
6
|
+
type SimpleViewModel,
|
|
5
7
|
} from "./view_model";
|
|
6
|
-
export {
|
|
8
|
+
export type { AttributeReturn, AR } from "./attribute_return";
|
|
9
|
+
export type { Action, Prelude, Meta, NamedDefinition } from "./symbols";
|
|
7
10
|
|
|
8
|
-
export {
|
|
11
|
+
export {
|
|
12
|
+
createBinding,
|
|
13
|
+
createDefine,
|
|
14
|
+
type SingleAttributeNode,
|
|
15
|
+
type NamedAttributesNode,
|
|
16
|
+
type View,
|
|
17
|
+
} from "./view";
|
package/src/symbols.ts
CHANGED
|
@@ -1,18 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
export type
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
export type Action = typeof Action;
|
|
6
|
-
|
|
7
|
-
export const NamedDefinition: unique symbol = Symbol("NamedDefinition");
|
|
8
|
-
export type NamedDefinition = typeof NamedDefinition;
|
|
9
|
-
|
|
10
|
-
export const Prelude: unique symbol = Symbol("Prelude");
|
|
11
|
-
export type Prelude = typeof Prelude;
|
|
12
|
-
|
|
13
|
-
export type AllSymbols = {
|
|
14
|
-
Meta: Meta;
|
|
15
|
-
Action: Action;
|
|
16
|
-
NamedDefinition: NamedDefinition;
|
|
17
|
-
Prelude: Prelude;
|
|
18
|
-
};
|
|
1
|
+
export type Meta = "~meta";
|
|
2
|
+
export type Action = "~action";
|
|
3
|
+
export type NamedDefinition = "~namedDefinition";
|
|
4
|
+
export type Prelude = "~prelude";
|
package/src/view.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AttributeBlockDefinition,
|
|
1
|
+
import type { AttributeBlockDefinition, IViewModel } from "./view_model";
|
|
2
2
|
|
|
3
3
|
export type AttributeName = string | symbol;
|
|
4
4
|
|
|
@@ -35,7 +35,7 @@ export class BindingContext {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export function createDefine(
|
|
38
|
-
rootVM:
|
|
38
|
+
rootVM: IViewModel<any, any>,
|
|
39
39
|
node: SingleAttributeNode,
|
|
40
40
|
): void {
|
|
41
41
|
const view = new View<any>({ attributes: [node] });
|
|
@@ -43,7 +43,7 @@ export function createDefine(
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function createBinding(
|
|
46
|
-
rootVM:
|
|
46
|
+
rootVM: IViewModel<any, any>,
|
|
47
47
|
node: SingleAttributeNode,
|
|
48
48
|
): unknown[] {
|
|
49
49
|
const bindingCtx = new BindingContext();
|
package/src/view_model.ts
CHANGED
|
@@ -1,58 +1,79 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AttributeReturn } from "./attribute_return";
|
|
2
|
+
import { Action, Meta, NamedDefinition } from "./symbols";
|
|
2
3
|
import { View } from "./view";
|
|
4
|
+
import { StandardJSONSchemaV1 } from "@standard-schema/spec";
|
|
3
5
|
|
|
4
6
|
export interface AttributeBlockDefinition {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
[Meta]: any;
|
|
7
|
+
"~action"?: AttributeDefinition | undefined;
|
|
8
|
+
"~meta": any;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
type Computed<T> = T extends infer U extends { [K in keyof T]: unknown }
|
|
11
12
|
? U
|
|
12
13
|
: never;
|
|
13
14
|
|
|
14
|
-
type BlockDefinitionRewriteMeta<
|
|
15
|
+
export type BlockDefinitionRewriteMeta<
|
|
15
16
|
BlockDef extends AttributeBlockDefinition,
|
|
16
17
|
NewMeta,
|
|
17
18
|
> =
|
|
18
|
-
Computed<Omit<BlockDef, Meta> & {
|
|
19
|
+
Computed<Omit<BlockDef, Meta> & { "~meta": NewMeta }> extends infer R extends
|
|
19
20
|
AttributeBlockDefinition
|
|
20
21
|
? R
|
|
21
22
|
: never;
|
|
22
23
|
|
|
23
|
-
export interface IViewModel<
|
|
24
|
-
|
|
24
|
+
export interface IViewModel<
|
|
25
|
+
ModelT,
|
|
26
|
+
BlockDef extends AttributeBlockDefinition,
|
|
27
|
+
CtorArgs extends any[],
|
|
28
|
+
> {
|
|
29
|
+
parse(
|
|
30
|
+
view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>,
|
|
31
|
+
...args: CtorArgs
|
|
32
|
+
): ModelT;
|
|
33
|
+
"~namedDefinition": BlockDef;
|
|
25
34
|
}
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
_symbols: AllSymbols;
|
|
33
|
-
[NamedDefinition]: BlockDef;
|
|
34
|
-
}
|
|
36
|
+
type LazyAttributeActionOrBinder<ModelT> = (
|
|
37
|
+
model: ModelT,
|
|
38
|
+
positionals: () => unknown[],
|
|
39
|
+
named: View<any>,
|
|
40
|
+
) => unknown;
|
|
35
41
|
|
|
36
|
-
|
|
42
|
+
class ViewModel<
|
|
37
43
|
ModelT,
|
|
38
44
|
BlockDef extends AttributeBlockDefinition,
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
CtorArgs extends any[],
|
|
46
|
+
> implements IViewModel<ModelT, BlockDef, CtorArgs> {
|
|
47
|
+
declare "~namedDefinition": BlockDef;
|
|
48
|
+
|
|
49
|
+
#registeredActions: Map<PropertyKey, LazyAttributeActionOrBinder<ModelT>> =
|
|
41
50
|
new Map();
|
|
42
|
-
#registeredBinders: Map<PropertyKey,
|
|
51
|
+
#registeredBinders: Map<PropertyKey, LazyAttributeActionOrBinder<ModelT>> =
|
|
43
52
|
new Map();
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
#Ctor: new (...args: CtorArgs) => ModelT;
|
|
46
55
|
|
|
47
|
-
|
|
48
|
-
this.#
|
|
56
|
+
constructor(Ctor: new (...args: CtorArgs) => ModelT) {
|
|
57
|
+
this.#Ctor = Ctor;
|
|
49
58
|
}
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
|
|
60
|
+
"~setActionOrBinder"(
|
|
61
|
+
context: "action" | "binder",
|
|
62
|
+
name: PropertyKey,
|
|
63
|
+
action: LazyAttributeActionOrBinder<ModelT>,
|
|
64
|
+
): void {
|
|
65
|
+
if (context === "action") {
|
|
66
|
+
this.#registeredActions.set(name, action);
|
|
67
|
+
} else {
|
|
68
|
+
this.#registeredBinders.set(name, action);
|
|
69
|
+
}
|
|
52
70
|
}
|
|
53
71
|
|
|
54
|
-
parse(
|
|
55
|
-
|
|
72
|
+
parse(
|
|
73
|
+
view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>,
|
|
74
|
+
...args: CtorArgs
|
|
75
|
+
): ModelT {
|
|
76
|
+
const model = new this.#Ctor(...args);
|
|
56
77
|
for (const attrNode of view._node.attributes) {
|
|
57
78
|
let { name, positionals, named, binding } = attrNode;
|
|
58
79
|
const insideBindingCtx = !!view._bindingCtx;
|
|
@@ -60,7 +81,10 @@ export class ViewModel<
|
|
|
60
81
|
insideBindingCtx ? this.#registeredBinders : this.#registeredActions
|
|
61
82
|
).get(name);
|
|
62
83
|
if (!insideBindingCtx && !fn) {
|
|
63
|
-
|
|
84
|
+
const modelName = this.#Ctor.name;
|
|
85
|
+
throw new Error(
|
|
86
|
+
`No action registered for attribute "${String(name)}" on model "${modelName}"`,
|
|
87
|
+
);
|
|
64
88
|
}
|
|
65
89
|
fn ??= () => {};
|
|
66
90
|
named ??= { attributes: [] };
|
|
@@ -74,29 +98,37 @@ export class ViewModel<
|
|
|
74
98
|
}
|
|
75
99
|
|
|
76
100
|
class AttributeDefHelper<ModelT> {
|
|
77
|
-
#viewModel: ViewModel<ModelT, any>;
|
|
78
|
-
constructor(viewModel: ViewModel<ModelT, any>) {
|
|
101
|
+
#viewModel: ViewModel<ModelT, any, any>;
|
|
102
|
+
constructor(viewModel: ViewModel<ModelT, any, any>) {
|
|
79
103
|
this.#viewModel = viewModel;
|
|
80
104
|
}
|
|
81
105
|
|
|
82
|
-
static readonly #
|
|
83
|
-
static readonly #
|
|
106
|
+
static readonly #lazyActionSlot: unique symbol = Symbol("actionSlot");
|
|
107
|
+
static readonly #lazyBinderSlot: unique symbol = Symbol("binderSlot");
|
|
84
108
|
|
|
85
|
-
|
|
86
|
-
for (const [name,
|
|
109
|
+
"~assignActions"(defResult: Partial<Record<string, unknown>>): void {
|
|
110
|
+
for (const [name, value] of Object.entries(defResult)) {
|
|
87
111
|
const actionDescriptor = Object.getOwnPropertyDescriptor(
|
|
88
|
-
|
|
89
|
-
AttributeDefHelper.#
|
|
112
|
+
value,
|
|
113
|
+
AttributeDefHelper.#lazyActionSlot,
|
|
90
114
|
);
|
|
91
115
|
if (actionDescriptor) {
|
|
92
|
-
this.#viewModel
|
|
116
|
+
this.#viewModel["~setActionOrBinder"](
|
|
117
|
+
"action",
|
|
118
|
+
name,
|
|
119
|
+
actionDescriptor.value,
|
|
120
|
+
);
|
|
93
121
|
}
|
|
94
122
|
const binderDescriptor = Object.getOwnPropertyDescriptor(
|
|
95
|
-
|
|
96
|
-
AttributeDefHelper.#
|
|
123
|
+
value,
|
|
124
|
+
AttributeDefHelper.#lazyBinderSlot,
|
|
97
125
|
);
|
|
98
126
|
if (binderDescriptor) {
|
|
99
|
-
this.#viewModel
|
|
127
|
+
this.#viewModel["~setActionOrBinder"](
|
|
128
|
+
"binder",
|
|
129
|
+
name,
|
|
130
|
+
binderDescriptor.value,
|
|
131
|
+
);
|
|
100
132
|
}
|
|
101
133
|
}
|
|
102
134
|
}
|
|
@@ -105,7 +137,7 @@ class AttributeDefHelper<ModelT> {
|
|
|
105
137
|
action: AttributeAction<ModelT, T>,
|
|
106
138
|
binder?:
|
|
107
139
|
| AttributeBinder<ModelT, T>
|
|
108
|
-
|
|
|
140
|
+
| IViewModel<any, ReturnType<T>["namedDefinition"], []>,
|
|
109
141
|
): T;
|
|
110
142
|
attribute<T extends AttributeDefinition>(
|
|
111
143
|
action: AttributeAction<ModelT, T>,
|
|
@@ -113,48 +145,117 @@ class AttributeDefHelper<ModelT> {
|
|
|
113
145
|
): T;
|
|
114
146
|
attribute(
|
|
115
147
|
action: any,
|
|
116
|
-
binder?: AttributeBinder<any, any> |
|
|
148
|
+
binder?: AttributeBinder<any, any> | IViewModel<any, any, any>,
|
|
117
149
|
) {
|
|
118
|
-
|
|
150
|
+
const returnValue = {};
|
|
151
|
+
const lazyAction: LazyAttributeActionOrBinder<ModelT> = (
|
|
152
|
+
model,
|
|
153
|
+
positionals,
|
|
154
|
+
named,
|
|
155
|
+
) => action(model, positionals(), named);
|
|
156
|
+
Object.defineProperty(returnValue, AttributeDefHelper.#lazyActionSlot, {
|
|
157
|
+
value: lazyAction,
|
|
158
|
+
enumerable: true,
|
|
159
|
+
});
|
|
160
|
+
let lazyBinder: LazyAttributeActionOrBinder<ModelT>;
|
|
161
|
+
if (typeof binder === "function") {
|
|
162
|
+
lazyBinder = (model, positionals, named) =>
|
|
163
|
+
binder(model, positionals(), named);
|
|
164
|
+
} else if (binder) {
|
|
119
165
|
const vm = binder;
|
|
120
|
-
|
|
166
|
+
lazyBinder = (model, positionals, named) => {
|
|
121
167
|
return vm.parse(named);
|
|
122
168
|
};
|
|
169
|
+
} else {
|
|
170
|
+
lazyBinder = () => {};
|
|
123
171
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
Object.defineProperty(returnValue, AttributeDefHelper.#actionSlot, {
|
|
127
|
-
value: action,
|
|
128
|
-
enumerable: true,
|
|
129
|
-
});
|
|
130
|
-
Object.defineProperty(returnValue, AttributeDefHelper.#binderSlot, {
|
|
131
|
-
value: binder,
|
|
172
|
+
Object.defineProperty(returnValue, AttributeDefHelper.#lazyBinderSlot, {
|
|
173
|
+
value: lazyBinder,
|
|
132
174
|
enumerable: true,
|
|
133
175
|
});
|
|
134
176
|
return returnValue;
|
|
135
177
|
}
|
|
178
|
+
|
|
179
|
+
simpleAttribute<Args extends any[]>(
|
|
180
|
+
action: (this: ModelT, ...args: Args) => void,
|
|
181
|
+
): {
|
|
182
|
+
(...args: Args): AttributeReturn.Done;
|
|
183
|
+
};
|
|
184
|
+
simpleAttribute<Args extends any[], U>(
|
|
185
|
+
action: (this: ModelT, ...args: Args) => void,
|
|
186
|
+
binder: (this: ModelT, ...args: Args) => U,
|
|
187
|
+
): {
|
|
188
|
+
(...args: Args): AttributeReturn.Done;
|
|
189
|
+
as?(): U;
|
|
190
|
+
};
|
|
191
|
+
simpleAttribute<Args extends any[], U>(
|
|
192
|
+
action: (this: ModelT, ...args: Args) => void,
|
|
193
|
+
binder?: (this: ModelT, ...args: Args) => U,
|
|
194
|
+
) {
|
|
195
|
+
const action2: AttributeAction<ModelT, any> = (model, positionals) =>
|
|
196
|
+
action.apply(model, positionals as Args);
|
|
197
|
+
let binder2: AttributeBinder<ModelT, any> | undefined;
|
|
198
|
+
if (binder) {
|
|
199
|
+
binder2 = (model, positionals) =>
|
|
200
|
+
binder.apply(model, positionals as Args);
|
|
201
|
+
}
|
|
202
|
+
return this.attribute<any>(action2, binder2);
|
|
203
|
+
}
|
|
136
204
|
}
|
|
137
205
|
|
|
138
206
|
export function defineViewModel<
|
|
139
207
|
T,
|
|
140
|
-
const BlockDef extends Record<string, AttributeDefinition
|
|
208
|
+
const BlockDef extends Partial<Record<string | Action, AttributeDefinition>>,
|
|
209
|
+
CtorArgs extends any[] = [],
|
|
141
210
|
InitMeta = void,
|
|
142
211
|
>(
|
|
143
|
-
Ctor: new () => T,
|
|
212
|
+
Ctor: new (...args: CtorArgs) => T,
|
|
144
213
|
modelDefFn: (helper: AttributeDefHelper<T>) => BlockDef,
|
|
145
214
|
initMeta?: InitMeta,
|
|
146
|
-
):
|
|
147
|
-
const vm = new ViewModel<T, BlockDef & {
|
|
215
|
+
): IViewModel<T, BlockDef & { "~meta": InitMeta }, CtorArgs> {
|
|
216
|
+
const vm = new ViewModel<T, BlockDef & { "~meta": InitMeta }, CtorArgs>(Ctor);
|
|
148
217
|
const helper = new AttributeDefHelper(vm);
|
|
149
218
|
const defResult = modelDefFn(helper);
|
|
150
|
-
helper
|
|
219
|
+
helper["~assignActions"](defResult);
|
|
220
|
+
return vm;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export type SimpleViewModel<T> = IViewModel<
|
|
224
|
+
T,
|
|
225
|
+
{
|
|
226
|
+
[K in keyof T]-?: {
|
|
227
|
+
(value: T[K]): AttributeReturn.Done;
|
|
228
|
+
uniqueKey(): K;
|
|
229
|
+
required(): {} extends Pick<T, K> ? false : true;
|
|
230
|
+
};
|
|
231
|
+
} & { "~meta": undefined },
|
|
232
|
+
[]
|
|
233
|
+
>;
|
|
234
|
+
|
|
235
|
+
export function defineSimpleViewModel<const T extends StandardJSONSchemaV1>(
|
|
236
|
+
schema: T,
|
|
237
|
+
): SimpleViewModel<StandardJSONSchemaV1.InferInput<T>> {
|
|
238
|
+
const jsonSchema = schema["~standard"].jsonSchema.input({
|
|
239
|
+
target: "draft-2020-12",
|
|
240
|
+
});
|
|
241
|
+
const Ctor = class SimpleViewModel {};
|
|
242
|
+
const vm = new ViewModel<any, any, []>(Ctor);
|
|
243
|
+
const helper = new AttributeDefHelper(vm);
|
|
244
|
+
const defResult: Record<string, any> = {};
|
|
245
|
+
for (const key of Object.keys(jsonSchema.properties ?? {})) {
|
|
246
|
+
defResult[key] = helper.simpleAttribute(function (this: any, value) {
|
|
247
|
+
this[key] = value;
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
helper["~assignActions"](defResult);
|
|
151
251
|
return vm;
|
|
152
252
|
}
|
|
153
253
|
|
|
154
|
-
interface AttributeDefinition {
|
|
254
|
+
export interface AttributeDefinition {
|
|
155
255
|
(...args: any[]): AttributePositionalReturnBase;
|
|
156
256
|
as?(): any;
|
|
157
|
-
|
|
257
|
+
required?(): boolean;
|
|
258
|
+
uniqueKey?(): string;
|
|
158
259
|
}
|
|
159
260
|
|
|
160
261
|
interface AttributePositionalReturnBase {
|
|
@@ -162,50 +263,22 @@ interface AttributePositionalReturnBase {
|
|
|
162
263
|
namedDefinition: AttributeBlockDefinition;
|
|
163
264
|
}
|
|
164
265
|
|
|
165
|
-
export namespace AttributeReturn {
|
|
166
|
-
export type This<TMeta = any> = {
|
|
167
|
-
[Meta]: TMeta;
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
export type EnableIf<Condition, T = void> = Condition extends true
|
|
171
|
-
? T
|
|
172
|
-
: never;
|
|
173
|
-
|
|
174
|
-
export type Done = {
|
|
175
|
-
namedDefinition: { [Meta]: void };
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
export type With<
|
|
179
|
-
VM extends ViewModel<any, any>,
|
|
180
|
-
TMeta = VM[NamedDefinition][Meta],
|
|
181
|
-
> = {
|
|
182
|
-
namedDefinition: BlockDefinitionRewriteMeta<VM[NamedDefinition], TMeta>;
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
export type WithRewriteMeta<VM extends ViewModel<any, any>, Meta> = {
|
|
186
|
-
namedDefinition: VM[NamedDefinition];
|
|
187
|
-
rewriteMeta: Meta;
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export type { AttributeReturn as AR };
|
|
192
|
-
|
|
193
266
|
export type AttributeAction<Model, T extends AttributeDefinition> = (
|
|
194
267
|
model: Model,
|
|
195
|
-
positional:
|
|
268
|
+
positional: Parameters<T>,
|
|
196
269
|
named: View<
|
|
197
270
|
ReturnType<T>["namedDefinition"] extends AttributeBlockDefinition
|
|
198
271
|
? ReturnType<T>["namedDefinition"]
|
|
199
|
-
: {
|
|
272
|
+
: { "~meta": void }
|
|
200
273
|
>,
|
|
201
274
|
) => void;
|
|
202
275
|
|
|
203
276
|
export type AttributeBinder<Model, T extends AttributeDefinition> = (
|
|
204
277
|
model: Model,
|
|
205
|
-
positional:
|
|
278
|
+
positional: Parameters<T>,
|
|
206
279
|
named: View<
|
|
207
280
|
ReturnType<T>["namedDefinition"] extends AttributeBlockDefinition
|
|
208
281
|
? ReturnType<T>["namedDefinition"]
|
|
209
|
-
: {
|
|
282
|
+
: { "~meta": void }
|
|
210
283
|
>,
|
|
211
284
|
) => T["as"] extends () => infer U ? U : void;
|