@gi-tcg/gts-runtime 0.2.0 → 0.3.0
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 +77 -70
- package/dist/index.js +62 -36
- 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 +144 -84
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,92 @@ 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
77
|
BlockDef extends AttributeBlockDefinition
|
|
54
78
|
> {
|
|
55
79
|
parse(view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>): ModelT;
|
|
80
|
+
"~namedDefinition": BlockDef;
|
|
56
81
|
}
|
|
57
|
-
|
|
58
|
-
ModelT,
|
|
59
|
-
BlockDef extends AttributeBlockDefinition
|
|
60
|
-
> {
|
|
61
|
-
/**
|
|
62
|
-
* Helper for fetching symbol types
|
|
63
|
-
* @internal
|
|
64
|
-
*/
|
|
65
|
-
_symbols: AllSymbols;
|
|
66
|
-
[NamedDefinition]: BlockDef;
|
|
67
|
-
}
|
|
82
|
+
type LazyAttributeActionOrBinder<ModelT> = (model: ModelT, positionals: () => unknown[], named: View<any>) => unknown;
|
|
68
83
|
declare class ViewModel<
|
|
69
84
|
ModelT,
|
|
70
85
|
BlockDef extends AttributeBlockDefinition
|
|
71
86
|
> implements IViewModel<ModelT, BlockDef> {
|
|
72
87
|
private;
|
|
73
|
-
|
|
88
|
+
"~namedDefinition": BlockDef;
|
|
74
89
|
constructor(Ctor: new () => ModelT);
|
|
75
|
-
|
|
76
|
-
_setBinder(name: PropertyKey, binder: AttributeBinder<ModelT, any>): void;
|
|
90
|
+
"~setActionOrBinder"(context: "action" | "binder", name: PropertyKey, action: LazyAttributeActionOrBinder<ModelT>): void;
|
|
77
91
|
parse(view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>): ModelT;
|
|
78
92
|
}
|
|
79
93
|
declare class AttributeDefHelper<ModelT> {
|
|
80
94
|
private;
|
|
81
95
|
constructor(viewModel: ViewModel<ModelT, any>);
|
|
82
|
-
|
|
96
|
+
"~assignActions"(defResult: Partial<Record<string, unknown>>): void;
|
|
83
97
|
attribute<T extends AttributeDefinition & {
|
|
84
98
|
as?: undefined;
|
|
85
|
-
}>(action: AttributeAction<ModelT, T>, binder?: AttributeBinder<ModelT, T> |
|
|
99
|
+
}>(action: AttributeAction<ModelT, T>, binder?: AttributeBinder<ModelT, T> | IViewModel<any, ReturnType<T>["namedDefinition"]>): T;
|
|
86
100
|
attribute<T extends AttributeDefinition>(action: AttributeAction<ModelT, T>, binder: AttributeBinder<ModelT, T>): T;
|
|
101
|
+
simpleAttribute<Args extends any[]>(action: (this: ModelT, ...args: Args) => void): {
|
|
102
|
+
(...args: Args): AttributeReturn.Done;
|
|
103
|
+
};
|
|
104
|
+
simpleAttribute<
|
|
105
|
+
Args extends any[],
|
|
106
|
+
U
|
|
107
|
+
>(action: (this: ModelT, ...args: Args) => void, binder: (this: ModelT, ...args: Args) => U): {
|
|
108
|
+
(...args: Args): AttributeReturn.Done;
|
|
109
|
+
as?(): U;
|
|
110
|
+
};
|
|
87
111
|
}
|
|
88
112
|
declare function defineViewModel<
|
|
89
113
|
T,
|
|
90
|
-
const BlockDef extends Record<string, AttributeDefinition
|
|
114
|
+
const BlockDef extends Partial<Record<string | Action, AttributeDefinition>>,
|
|
91
115
|
InitMeta = void
|
|
92
|
-
>(Ctor: new () => T, modelDefFn: (helper: AttributeDefHelper<T>) => BlockDef, initMeta?: InitMeta):
|
|
93
|
-
|
|
116
|
+
>(Ctor: new () => T, modelDefFn: (helper: AttributeDefHelper<T>) => BlockDef, initMeta?: InitMeta): IViewModel<T, BlockDef & {
|
|
117
|
+
"~meta": InitMeta;
|
|
118
|
+
}>;
|
|
119
|
+
type SimpleViewModel<T> = IViewModel<T, { [K in keyof T]-? : {
|
|
120
|
+
(value: T[K]): AttributeReturn.Done;
|
|
121
|
+
uniqueKey(): K;
|
|
122
|
+
required(): {} extends Pick<T, K> ? false : true;
|
|
123
|
+
} } & {
|
|
124
|
+
"~meta": undefined;
|
|
94
125
|
}>;
|
|
126
|
+
declare function defineSimpleViewModel<const T extends StandardJSONSchemaV1>(schema: T): SimpleViewModel<StandardJSONSchemaV1.InferInput<T>>;
|
|
95
127
|
interface AttributeDefinition {
|
|
96
128
|
(...args: any[]): AttributePositionalReturnBase;
|
|
97
129
|
as?(): any;
|
|
130
|
+
required?(): boolean;
|
|
131
|
+
uniqueKey?(): string;
|
|
98
132
|
}
|
|
99
133
|
interface AttributePositionalReturnBase {
|
|
100
134
|
rewriteMeta?: any;
|
|
101
135
|
namedDefinition: AttributeBlockDefinition;
|
|
102
136
|
}
|
|
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
137
|
type AttributeAction<
|
|
131
138
|
Model,
|
|
132
139
|
T extends AttributeDefinition
|
|
133
|
-
> = (model: Model, positional:
|
|
134
|
-
|
|
140
|
+
> = (model: Model, positional: Parameters<T>, named: View<ReturnType<T>["namedDefinition"] extends AttributeBlockDefinition ? ReturnType<T>["namedDefinition"] : {
|
|
141
|
+
"~meta": void;
|
|
135
142
|
}>) => void;
|
|
136
143
|
type AttributeBinder<
|
|
137
144
|
Model,
|
|
138
145
|
T extends AttributeDefinition
|
|
139
|
-
> = (model: Model, positional:
|
|
140
|
-
|
|
146
|
+
> = (model: Model, positional: Parameters<T>, named: View<ReturnType<T>["namedDefinition"] extends AttributeBlockDefinition ? ReturnType<T>["namedDefinition"] : {
|
|
147
|
+
"~meta": void;
|
|
141
148
|
}>) => T["as"] extends () => infer U ? U : void;
|
|
142
|
-
export { defineViewModel, createDefine, createBinding, Prelude, IViewModel, AttributeReturn, Action };
|
|
149
|
+
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
47
|
parse(view) {
|
|
47
|
-
const model = new this
|
|
48
|
+
const model = new this.#Ctor;
|
|
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.0",
|
|
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,68 @@
|
|
|
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
24
|
export interface IViewModel<ModelT, BlockDef extends AttributeBlockDefinition> {
|
|
24
25
|
parse(view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>): ModelT;
|
|
26
|
+
"~namedDefinition": BlockDef;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
_symbols: AllSymbols;
|
|
33
|
-
[NamedDefinition]: BlockDef;
|
|
34
|
-
}
|
|
29
|
+
type LazyAttributeActionOrBinder<ModelT> = (
|
|
30
|
+
model: ModelT,
|
|
31
|
+
positionals: () => unknown[],
|
|
32
|
+
named: View<any>,
|
|
33
|
+
) => unknown;
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
class ViewModel<
|
|
37
36
|
ModelT,
|
|
38
37
|
BlockDef extends AttributeBlockDefinition,
|
|
39
38
|
> implements IViewModel<ModelT, BlockDef> {
|
|
40
|
-
|
|
39
|
+
declare "~namedDefinition": BlockDef;
|
|
40
|
+
|
|
41
|
+
#registeredActions: Map<PropertyKey, LazyAttributeActionOrBinder<ModelT>> =
|
|
41
42
|
new Map();
|
|
42
|
-
#registeredBinders: Map<PropertyKey,
|
|
43
|
+
#registeredBinders: Map<PropertyKey, LazyAttributeActionOrBinder<ModelT>> =
|
|
43
44
|
new Map();
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
#Ctor: new () => ModelT;
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
this.#
|
|
48
|
+
constructor(Ctor: new () => ModelT) {
|
|
49
|
+
this.#Ctor = Ctor;
|
|
49
50
|
}
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
|
|
52
|
+
"~setActionOrBinder"(
|
|
53
|
+
context: "action" | "binder",
|
|
54
|
+
name: PropertyKey,
|
|
55
|
+
action: LazyAttributeActionOrBinder<ModelT>,
|
|
56
|
+
): void {
|
|
57
|
+
if (context === "action") {
|
|
58
|
+
this.#registeredActions.set(name, action);
|
|
59
|
+
} else {
|
|
60
|
+
this.#registeredBinders.set(name, action);
|
|
61
|
+
}
|
|
52
62
|
}
|
|
53
63
|
|
|
54
64
|
parse(view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>): ModelT {
|
|
55
|
-
const model = new this
|
|
65
|
+
const model = new this.#Ctor();
|
|
56
66
|
for (const attrNode of view._node.attributes) {
|
|
57
67
|
let { name, positionals, named, binding } = attrNode;
|
|
58
68
|
const insideBindingCtx = !!view._bindingCtx;
|
|
@@ -60,7 +70,10 @@ export class ViewModel<
|
|
|
60
70
|
insideBindingCtx ? this.#registeredBinders : this.#registeredActions
|
|
61
71
|
).get(name);
|
|
62
72
|
if (!insideBindingCtx && !fn) {
|
|
63
|
-
|
|
73
|
+
const modelName = this.#Ctor.name;
|
|
74
|
+
throw new Error(
|
|
75
|
+
`No action registered for attribute "${String(name)}" on model "${modelName}"`,
|
|
76
|
+
);
|
|
64
77
|
}
|
|
65
78
|
fn ??= () => {};
|
|
66
79
|
named ??= { attributes: [] };
|
|
@@ -79,24 +92,32 @@ class AttributeDefHelper<ModelT> {
|
|
|
79
92
|
this.#viewModel = viewModel;
|
|
80
93
|
}
|
|
81
94
|
|
|
82
|
-
static readonly #
|
|
83
|
-
static readonly #
|
|
95
|
+
static readonly #lazyActionSlot: unique symbol = Symbol("actionSlot");
|
|
96
|
+
static readonly #lazyBinderSlot: unique symbol = Symbol("binderSlot");
|
|
84
97
|
|
|
85
|
-
|
|
86
|
-
for (const [name,
|
|
98
|
+
"~assignActions"(defResult: Partial<Record<string, unknown>>): void {
|
|
99
|
+
for (const [name, value] of Object.entries(defResult)) {
|
|
87
100
|
const actionDescriptor = Object.getOwnPropertyDescriptor(
|
|
88
|
-
|
|
89
|
-
AttributeDefHelper.#
|
|
101
|
+
value,
|
|
102
|
+
AttributeDefHelper.#lazyActionSlot,
|
|
90
103
|
);
|
|
91
104
|
if (actionDescriptor) {
|
|
92
|
-
this.#viewModel
|
|
105
|
+
this.#viewModel["~setActionOrBinder"](
|
|
106
|
+
"action",
|
|
107
|
+
name,
|
|
108
|
+
actionDescriptor.value,
|
|
109
|
+
);
|
|
93
110
|
}
|
|
94
111
|
const binderDescriptor = Object.getOwnPropertyDescriptor(
|
|
95
|
-
|
|
96
|
-
AttributeDefHelper.#
|
|
112
|
+
value,
|
|
113
|
+
AttributeDefHelper.#lazyBinderSlot,
|
|
97
114
|
);
|
|
98
115
|
if (binderDescriptor) {
|
|
99
|
-
this.#viewModel
|
|
116
|
+
this.#viewModel["~setActionOrBinder"](
|
|
117
|
+
"binder",
|
|
118
|
+
name,
|
|
119
|
+
binderDescriptor.value,
|
|
120
|
+
);
|
|
100
121
|
}
|
|
101
122
|
}
|
|
102
123
|
}
|
|
@@ -105,7 +126,7 @@ class AttributeDefHelper<ModelT> {
|
|
|
105
126
|
action: AttributeAction<ModelT, T>,
|
|
106
127
|
binder?:
|
|
107
128
|
| AttributeBinder<ModelT, T>
|
|
108
|
-
|
|
|
129
|
+
| IViewModel<any, ReturnType<T>["namedDefinition"]>,
|
|
109
130
|
): T;
|
|
110
131
|
attribute<T extends AttributeDefinition>(
|
|
111
132
|
action: AttributeAction<ModelT, T>,
|
|
@@ -113,48 +134,115 @@ class AttributeDefHelper<ModelT> {
|
|
|
113
134
|
): T;
|
|
114
135
|
attribute(
|
|
115
136
|
action: any,
|
|
116
|
-
binder?: AttributeBinder<any, any> |
|
|
137
|
+
binder?: AttributeBinder<any, any> | IViewModel<any, any>,
|
|
117
138
|
) {
|
|
118
|
-
|
|
139
|
+
const returnValue = {};
|
|
140
|
+
const lazyAction: LazyAttributeActionOrBinder<ModelT> = (
|
|
141
|
+
model,
|
|
142
|
+
positionals,
|
|
143
|
+
named,
|
|
144
|
+
) => action(model, positionals(), named);
|
|
145
|
+
Object.defineProperty(returnValue, AttributeDefHelper.#lazyActionSlot, {
|
|
146
|
+
value: lazyAction,
|
|
147
|
+
enumerable: true,
|
|
148
|
+
});
|
|
149
|
+
let lazyBinder: LazyAttributeActionOrBinder<ModelT>;
|
|
150
|
+
if (typeof binder === "function") {
|
|
151
|
+
lazyBinder = (model, positionals, named) =>
|
|
152
|
+
binder(model, positionals(), named);
|
|
153
|
+
} else if (binder) {
|
|
119
154
|
const vm = binder;
|
|
120
|
-
|
|
155
|
+
lazyBinder = (model, positionals, named) => {
|
|
121
156
|
return vm.parse(named);
|
|
122
157
|
};
|
|
158
|
+
} else {
|
|
159
|
+
lazyBinder = () => {};
|
|
123
160
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
Object.defineProperty(returnValue, AttributeDefHelper.#actionSlot, {
|
|
127
|
-
value: action,
|
|
128
|
-
enumerable: true,
|
|
129
|
-
});
|
|
130
|
-
Object.defineProperty(returnValue, AttributeDefHelper.#binderSlot, {
|
|
131
|
-
value: binder,
|
|
161
|
+
Object.defineProperty(returnValue, AttributeDefHelper.#lazyBinderSlot, {
|
|
162
|
+
value: lazyBinder,
|
|
132
163
|
enumerable: true,
|
|
133
164
|
});
|
|
134
165
|
return returnValue;
|
|
135
166
|
}
|
|
167
|
+
|
|
168
|
+
simpleAttribute<Args extends any[]>(
|
|
169
|
+
action: (this: ModelT, ...args: Args) => void,
|
|
170
|
+
): {
|
|
171
|
+
(...args: Args): AttributeReturn.Done;
|
|
172
|
+
};
|
|
173
|
+
simpleAttribute<Args extends any[], U>(
|
|
174
|
+
action: (this: ModelT, ...args: Args) => void,
|
|
175
|
+
binder: (this: ModelT, ...args: Args) => U,
|
|
176
|
+
): {
|
|
177
|
+
(...args: Args): AttributeReturn.Done;
|
|
178
|
+
as?(): U;
|
|
179
|
+
};
|
|
180
|
+
simpleAttribute<Args extends any[], U>(
|
|
181
|
+
action: (this: ModelT, ...args: Args) => void,
|
|
182
|
+
binder?: (this: ModelT, ...args: Args) => U,
|
|
183
|
+
) {
|
|
184
|
+
const action2: AttributeAction<ModelT, any> = (model, positionals) =>
|
|
185
|
+
action.apply(model, positionals as Args);
|
|
186
|
+
let binder2: AttributeBinder<ModelT, any> | undefined;
|
|
187
|
+
if (binder) {
|
|
188
|
+
binder2 = (model, positionals) =>
|
|
189
|
+
binder.apply(model, positionals as Args);
|
|
190
|
+
}
|
|
191
|
+
return this.attribute<any>(action2, binder2);
|
|
192
|
+
}
|
|
136
193
|
}
|
|
137
194
|
|
|
138
195
|
export function defineViewModel<
|
|
139
196
|
T,
|
|
140
|
-
const BlockDef extends Record<string, AttributeDefinition
|
|
197
|
+
const BlockDef extends Partial<Record<string | Action, AttributeDefinition>>,
|
|
141
198
|
InitMeta = void,
|
|
142
199
|
>(
|
|
143
200
|
Ctor: new () => T,
|
|
144
201
|
modelDefFn: (helper: AttributeDefHelper<T>) => BlockDef,
|
|
145
202
|
initMeta?: InitMeta,
|
|
146
|
-
):
|
|
147
|
-
const vm = new ViewModel<T, BlockDef & {
|
|
203
|
+
): IViewModel<T, BlockDef & { "~meta": InitMeta }> {
|
|
204
|
+
const vm = new ViewModel<T, BlockDef & { "~meta": InitMeta }>(Ctor);
|
|
148
205
|
const helper = new AttributeDefHelper(vm);
|
|
149
206
|
const defResult = modelDefFn(helper);
|
|
150
|
-
helper
|
|
207
|
+
helper["~assignActions"](defResult);
|
|
208
|
+
return vm;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export type SimpleViewModel<T> = IViewModel<
|
|
212
|
+
T,
|
|
213
|
+
{
|
|
214
|
+
[K in keyof T]-?: {
|
|
215
|
+
(value: T[K]): AttributeReturn.Done;
|
|
216
|
+
uniqueKey(): K;
|
|
217
|
+
required(): {} extends Pick<T, K> ? false : true;
|
|
218
|
+
};
|
|
219
|
+
} & { "~meta": undefined }
|
|
220
|
+
>;
|
|
221
|
+
|
|
222
|
+
export function defineSimpleViewModel<const T extends StandardJSONSchemaV1>(
|
|
223
|
+
schema: T,
|
|
224
|
+
): SimpleViewModel<StandardJSONSchemaV1.InferInput<T>> {
|
|
225
|
+
const jsonSchema = schema["~standard"].jsonSchema.input({
|
|
226
|
+
target: "draft-2020-12",
|
|
227
|
+
});
|
|
228
|
+
const Ctor = class SimpleViewModel {};
|
|
229
|
+
const vm = new ViewModel<any, any>(Ctor);
|
|
230
|
+
const helper = new AttributeDefHelper(vm);
|
|
231
|
+
const defResult: Record<string, any> = {};
|
|
232
|
+
for (const key of Object.keys(jsonSchema.properties ?? {})) {
|
|
233
|
+
defResult[key] = helper.simpleAttribute(function (this: any, value) {
|
|
234
|
+
this[key] = value;
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
helper["~assignActions"](defResult);
|
|
151
238
|
return vm;
|
|
152
239
|
}
|
|
153
240
|
|
|
154
|
-
interface AttributeDefinition {
|
|
241
|
+
export interface AttributeDefinition {
|
|
155
242
|
(...args: any[]): AttributePositionalReturnBase;
|
|
156
243
|
as?(): any;
|
|
157
|
-
|
|
244
|
+
required?(): boolean;
|
|
245
|
+
uniqueKey?(): string;
|
|
158
246
|
}
|
|
159
247
|
|
|
160
248
|
interface AttributePositionalReturnBase {
|
|
@@ -162,50 +250,22 @@ interface AttributePositionalReturnBase {
|
|
|
162
250
|
namedDefinition: AttributeBlockDefinition;
|
|
163
251
|
}
|
|
164
252
|
|
|
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
253
|
export type AttributeAction<Model, T extends AttributeDefinition> = (
|
|
194
254
|
model: Model,
|
|
195
|
-
positional:
|
|
255
|
+
positional: Parameters<T>,
|
|
196
256
|
named: View<
|
|
197
257
|
ReturnType<T>["namedDefinition"] extends AttributeBlockDefinition
|
|
198
258
|
? ReturnType<T>["namedDefinition"]
|
|
199
|
-
: {
|
|
259
|
+
: { "~meta": void }
|
|
200
260
|
>,
|
|
201
261
|
) => void;
|
|
202
262
|
|
|
203
263
|
export type AttributeBinder<Model, T extends AttributeDefinition> = (
|
|
204
264
|
model: Model,
|
|
205
|
-
positional:
|
|
265
|
+
positional: Parameters<T>,
|
|
206
266
|
named: View<
|
|
207
267
|
ReturnType<T>["namedDefinition"] extends AttributeBlockDefinition
|
|
208
268
|
? ReturnType<T>["namedDefinition"]
|
|
209
|
-
: {
|
|
269
|
+
: { "~meta": void }
|
|
210
270
|
>,
|
|
211
271
|
) => T["as"] extends () => infer U ? U : void;
|