@gi-tcg/gts-runtime 0.2.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.
@@ -0,0 +1,142 @@
1
+ declare const Meta: unique symbol;
2
+ type Meta = typeof Meta;
3
+ declare const Action: unique symbol;
4
+ type Action = typeof Action;
5
+ type NamedDefinition = typeof NamedDefinition;
6
+ declare const Prelude: unique symbol;
7
+ type Prelude = typeof Prelude;
8
+ type AllSymbols = {
9
+ Meta: Meta;
10
+ Action: Action;
11
+ NamedDefinition: NamedDefinition;
12
+ Prelude: Prelude;
13
+ };
14
+ type AttributeName = string | symbol;
15
+ interface SingleAttributeNode {
16
+ name: AttributeName;
17
+ /** Lazy evaluation: only evaluate positional expressions when traversing */
18
+ positionals: () => any[];
19
+ named: NamedAttributesNode | null;
20
+ /** Marks this attribute as a binding candidate */
21
+ binding?: "public" | "private";
22
+ }
23
+ interface NamedAttributesNode {
24
+ attributes: SingleAttributeNode[];
25
+ }
26
+ declare class View<BlockDef extends AttributeBlockDefinition> {
27
+ private;
28
+ _node: NamedAttributesNode;
29
+ _bindingCtx?: BindingContext | undefined;
30
+ constructor(_node: NamedAttributesNode, _bindingCtx?: BindingContext | undefined);
31
+ }
32
+ declare class BindingContext {
33
+ private;
34
+ addBinding(value: unknown): void;
35
+ getBindings(): unknown[];
36
+ }
37
+ declare function createDefine(rootVM: ViewModel<any, any>, node: SingleAttributeNode): void;
38
+ declare function createBinding(rootVM: ViewModel<any, any>, node: SingleAttributeNode): unknown[];
39
+ interface AttributeBlockDefinition {
40
+ [name: string]: AttributeDefinition;
41
+ [Action]?: AttributeDefinition;
42
+ [Meta]: any;
43
+ }
44
+ type Computed<T> = T extends infer U extends { [K in keyof T] : unknown } ? U : never;
45
+ type BlockDefinitionRewriteMeta<
46
+ BlockDef extends AttributeBlockDefinition,
47
+ NewMeta
48
+ > = Computed<Omit<BlockDef, Meta> & {
49
+ [Meta]: NewMeta;
50
+ }> extends infer R extends AttributeBlockDefinition ? R : never;
51
+ interface IViewModel<
52
+ ModelT,
53
+ BlockDef extends AttributeBlockDefinition
54
+ > {
55
+ parse(view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>): ModelT;
56
+ }
57
+ interface ViewModel<
58
+ ModelT,
59
+ BlockDef extends AttributeBlockDefinition
60
+ > {
61
+ /**
62
+ * Helper for fetching symbol types
63
+ * @internal
64
+ */
65
+ _symbols: AllSymbols;
66
+ [NamedDefinition]: BlockDef;
67
+ }
68
+ declare class ViewModel<
69
+ ModelT,
70
+ BlockDef extends AttributeBlockDefinition
71
+ > implements IViewModel<ModelT, BlockDef> {
72
+ private;
73
+ private Ctor;
74
+ constructor(Ctor: new () => ModelT);
75
+ _setAction(name: PropertyKey, action: AttributeAction<ModelT, any>): void;
76
+ _setBinder(name: PropertyKey, binder: AttributeBinder<ModelT, any>): void;
77
+ parse(view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>): ModelT;
78
+ }
79
+ declare class AttributeDefHelper<ModelT> {
80
+ private;
81
+ constructor(viewModel: ViewModel<ModelT, any>);
82
+ _assignActions(defResult: Record<string, unknown>): void;
83
+ attribute<T extends AttributeDefinition & {
84
+ as?: undefined;
85
+ }>(action: AttributeAction<ModelT, T>, binder?: AttributeBinder<ModelT, T> | ViewModel<any, ReturnType<T>["namedDefinition"]>): T;
86
+ attribute<T extends AttributeDefinition>(action: AttributeAction<ModelT, T>, binder: AttributeBinder<ModelT, T>): T;
87
+ }
88
+ declare function defineViewModel<
89
+ T,
90
+ const BlockDef extends Record<string, AttributeDefinition>,
91
+ InitMeta = void
92
+ >(Ctor: new () => T, modelDefFn: (helper: AttributeDefHelper<T>) => BlockDef, initMeta?: InitMeta): ViewModel<T, BlockDef & {
93
+ [Meta]: InitMeta;
94
+ }>;
95
+ interface AttributeDefinition {
96
+ (...args: any[]): AttributePositionalReturnBase;
97
+ as?(): any;
98
+ }
99
+ interface AttributePositionalReturnBase {
100
+ rewriteMeta?: any;
101
+ namedDefinition: AttributeBlockDefinition;
102
+ }
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
+ type AttributeAction<
131
+ Model,
132
+ T extends AttributeDefinition
133
+ > = (model: Model, positional: () => Parameters<T>, named: View<ReturnType<T>["namedDefinition"] extends AttributeBlockDefinition ? ReturnType<T>["namedDefinition"] : {
134
+ [Meta]: void;
135
+ }>) => void;
136
+ type AttributeBinder<
137
+ Model,
138
+ T extends AttributeDefinition
139
+ > = (model: Model, positional: () => Parameters<T>, named: View<ReturnType<T>["namedDefinition"] extends AttributeBlockDefinition ? ReturnType<T>["namedDefinition"] : {
140
+ [Meta]: void;
141
+ }>) => T["as"] extends () => infer U ? U : void;
142
+ export { defineViewModel, createDefine, createBinding, Prelude, IViewModel, AttributeReturn, Action };
package/dist/index.js ADDED
@@ -0,0 +1,123 @@
1
+ // packages/runtime/src/view.ts
2
+ class View {
3
+ _node;
4
+ _bindingCtx;
5
+ #phantom;
6
+ constructor(_node, _bindingCtx) {
7
+ this._node = _node;
8
+ this._bindingCtx = _bindingCtx;
9
+ }
10
+ }
11
+
12
+ class BindingContext {
13
+ #bindings = [];
14
+ addBinding(value) {
15
+ this.#bindings.push(value);
16
+ }
17
+ getBindings() {
18
+ return this.#bindings;
19
+ }
20
+ }
21
+ function createDefine(rootVM, node) {
22
+ const view = new View({ attributes: [node] });
23
+ rootVM.parse(view);
24
+ }
25
+ function createBinding(rootVM, node) {
26
+ const bindingCtx = new BindingContext;
27
+ const view = new View({ attributes: [node] }, bindingCtx);
28
+ rootVM.parse(view);
29
+ return bindingCtx.getBindings();
30
+ }
31
+
32
+ // packages/runtime/src/view_model.ts
33
+ class ViewModel {
34
+ Ctor;
35
+ #registeredActions = new Map;
36
+ #registeredBinders = new Map;
37
+ constructor(Ctor) {
38
+ this.Ctor = Ctor;
39
+ }
40
+ _setAction(name, action) {
41
+ this.#registeredActions.set(name, action);
42
+ }
43
+ _setBinder(name, binder) {
44
+ this.#registeredBinders.set(name, binder);
45
+ }
46
+ parse(view) {
47
+ const model = new this.Ctor;
48
+ for (const attrNode of view._node.attributes) {
49
+ let { name, positionals, named, binding } = attrNode;
50
+ const insideBindingCtx = !!view._bindingCtx;
51
+ let fn = (insideBindingCtx ? this.#registeredBinders : this.#registeredActions).get(name);
52
+ if (!insideBindingCtx && !fn) {
53
+ throw new Error(`No action registered for attribute: ${String(name)}`);
54
+ }
55
+ fn ??= () => {};
56
+ named ??= { attributes: [] };
57
+ const value = fn(model, positionals, new View(named, view._bindingCtx));
58
+ if (binding && view._bindingCtx) {
59
+ view._bindingCtx.addBinding(value);
60
+ }
61
+ }
62
+ return model;
63
+ }
64
+ }
65
+
66
+ class AttributeDefHelper {
67
+ #viewModel;
68
+ constructor(viewModel) {
69
+ this.#viewModel = viewModel;
70
+ }
71
+ static #actionSlot = Symbol("actionSlot");
72
+ static #binderSlot = Symbol("binderSlot");
73
+ _assignActions(defResult) {
74
+ for (const [name, returnValue] of Object.entries(defResult)) {
75
+ const actionDescriptor = Object.getOwnPropertyDescriptor(returnValue, AttributeDefHelper.#actionSlot);
76
+ if (actionDescriptor) {
77
+ this.#viewModel._setAction(name, actionDescriptor.value);
78
+ }
79
+ const binderDescriptor = Object.getOwnPropertyDescriptor(returnValue, AttributeDefHelper.#binderSlot);
80
+ if (binderDescriptor) {
81
+ this.#viewModel._setBinder(name, binderDescriptor.value);
82
+ }
83
+ }
84
+ }
85
+ attribute(action, binder) {
86
+ if (binder instanceof ViewModel) {
87
+ const vm = binder;
88
+ binder = (model, positionals, named) => {
89
+ return vm.parse(named);
90
+ };
91
+ }
92
+ binder ??= () => {};
93
+ const returnValue = {};
94
+ Object.defineProperty(returnValue, AttributeDefHelper.#actionSlot, {
95
+ value: action,
96
+ enumerable: true
97
+ });
98
+ Object.defineProperty(returnValue, AttributeDefHelper.#binderSlot, {
99
+ value: binder,
100
+ enumerable: true
101
+ });
102
+ return returnValue;
103
+ }
104
+ }
105
+ function defineViewModel(Ctor, modelDefFn, initMeta) {
106
+ const vm = new ViewModel(Ctor);
107
+ const helper = new AttributeDefHelper(vm);
108
+ const defResult = modelDefFn(helper);
109
+ helper._assignActions(defResult);
110
+ return vm;
111
+ }
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
+ export {
118
+ defineViewModel,
119
+ createDefine,
120
+ createBinding,
121
+ Prelude,
122
+ Action
123
+ };
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@gi-tcg/gts-runtime",
3
+ "version": "0.2.0",
4
+ "repository": "https://github.com/piovium/gts.git",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "bun": "./src/index.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "src",
16
+ "dist"
17
+ ],
18
+ "dependencies": {},
19
+ "devDependencies": {}
20
+ }
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ export {
2
+ defineViewModel,
3
+ type IViewModel,
4
+ type AttributeReturn,
5
+ } from "./view_model";
6
+ export { Action, Prelude } from "./symbols";
7
+
8
+ export { createBinding, createDefine } from "./view";
package/src/symbols.ts ADDED
@@ -0,0 +1,18 @@
1
+ export const Meta: unique symbol = Symbol("Meta");
2
+ export type Meta = typeof Meta;
3
+
4
+ export const Action: unique symbol = Symbol("Action");
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
+ };
package/src/view.ts ADDED
@@ -0,0 +1,53 @@
1
+ import type { AttributeBlockDefinition, ViewModel } from "./view_model";
2
+
3
+ export type AttributeName = string | symbol;
4
+
5
+ export interface SingleAttributeNode {
6
+ name: AttributeName;
7
+ /** Lazy evaluation: only evaluate positional expressions when traversing */
8
+ positionals: () => any[];
9
+ named: NamedAttributesNode | null;
10
+ /** Marks this attribute as a binding export candidate */
11
+ binding?: "public" | "private";
12
+ }
13
+
14
+ export interface NamedAttributesNode {
15
+ attributes: SingleAttributeNode[];
16
+ }
17
+
18
+ export class View<BlockDef extends AttributeBlockDefinition> {
19
+ #phantom!: BlockDef;
20
+
21
+ constructor(
22
+ public _node: NamedAttributesNode,
23
+ public _bindingCtx?: BindingContext | undefined,
24
+ ) {}
25
+ }
26
+
27
+ export class BindingContext {
28
+ #bindings: unknown[] = [];
29
+ addBinding(value: unknown): void {
30
+ this.#bindings.push(value);
31
+ }
32
+ getBindings(): unknown[] {
33
+ return this.#bindings;
34
+ }
35
+ }
36
+
37
+ export function createDefine(
38
+ rootVM: ViewModel<any, any>,
39
+ node: SingleAttributeNode,
40
+ ): void {
41
+ const view = new View<any>({ attributes: [node] });
42
+ rootVM.parse(view);
43
+ }
44
+
45
+ export function createBinding(
46
+ rootVM: ViewModel<any, any>,
47
+ node: SingleAttributeNode,
48
+ ): unknown[] {
49
+ const bindingCtx = new BindingContext();
50
+ const view = new View<any>({ attributes: [node] }, bindingCtx);
51
+ rootVM.parse(view);
52
+ return bindingCtx.getBindings();
53
+ }
@@ -0,0 +1,211 @@
1
+ import { Action, AllSymbols, Meta, NamedDefinition } from "./symbols";
2
+ import { View } from "./view";
3
+
4
+ export interface AttributeBlockDefinition {
5
+ [name: string]: AttributeDefinition;
6
+ [Action]?: AttributeDefinition;
7
+ [Meta]: any;
8
+ }
9
+
10
+ type Computed<T> = T extends infer U extends { [K in keyof T]: unknown }
11
+ ? U
12
+ : never;
13
+
14
+ type BlockDefinitionRewriteMeta<
15
+ BlockDef extends AttributeBlockDefinition,
16
+ NewMeta,
17
+ > =
18
+ Computed<Omit<BlockDef, Meta> & { [Meta]: NewMeta }> extends infer R extends
19
+ AttributeBlockDefinition
20
+ ? R
21
+ : never;
22
+
23
+ export interface IViewModel<ModelT, BlockDef extends AttributeBlockDefinition> {
24
+ parse(view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>): ModelT;
25
+ }
26
+
27
+ export interface ViewModel<ModelT, BlockDef extends AttributeBlockDefinition> {
28
+ /**
29
+ * Helper for fetching symbol types
30
+ * @internal
31
+ */
32
+ _symbols: AllSymbols;
33
+ [NamedDefinition]: BlockDef;
34
+ }
35
+
36
+ export class ViewModel<
37
+ ModelT,
38
+ BlockDef extends AttributeBlockDefinition,
39
+ > implements IViewModel<ModelT, BlockDef> {
40
+ #registeredActions: Map<PropertyKey, AttributeAction<ModelT, any>> =
41
+ new Map();
42
+ #registeredBinders: Map<PropertyKey, AttributeBinder<ModelT, any>> =
43
+ new Map();
44
+
45
+ constructor(private Ctor: new () => ModelT) {}
46
+
47
+ _setAction(name: PropertyKey, action: AttributeAction<ModelT, any>): void {
48
+ this.#registeredActions.set(name, action);
49
+ }
50
+ _setBinder(name: PropertyKey, binder: AttributeBinder<ModelT, any>): void {
51
+ this.#registeredBinders.set(name, binder);
52
+ }
53
+
54
+ parse(view: View<BlockDefinitionRewriteMeta<BlockDef, unknown>>): ModelT {
55
+ const model = new this.Ctor();
56
+ for (const attrNode of view._node.attributes) {
57
+ let { name, positionals, named, binding } = attrNode;
58
+ const insideBindingCtx = !!view._bindingCtx;
59
+ let fn = (
60
+ insideBindingCtx ? this.#registeredBinders : this.#registeredActions
61
+ ).get(name);
62
+ if (!insideBindingCtx && !fn) {
63
+ throw new Error(`No action registered for attribute: ${String(name)}`);
64
+ }
65
+ fn ??= () => {};
66
+ named ??= { attributes: [] };
67
+ const value = fn(model, positionals, new View(named, view._bindingCtx));
68
+ if (binding && view._bindingCtx) {
69
+ view._bindingCtx.addBinding(value);
70
+ }
71
+ }
72
+ return model;
73
+ }
74
+ }
75
+
76
+ class AttributeDefHelper<ModelT> {
77
+ #viewModel: ViewModel<ModelT, any>;
78
+ constructor(viewModel: ViewModel<ModelT, any>) {
79
+ this.#viewModel = viewModel;
80
+ }
81
+
82
+ static readonly #actionSlot: unique symbol = Symbol("actionSlot");
83
+ static readonly #binderSlot: unique symbol = Symbol("binderSlot");
84
+
85
+ _assignActions(defResult: Record<string, unknown>): void {
86
+ for (const [name, returnValue] of Object.entries(defResult)) {
87
+ const actionDescriptor = Object.getOwnPropertyDescriptor(
88
+ returnValue,
89
+ AttributeDefHelper.#actionSlot,
90
+ );
91
+ if (actionDescriptor) {
92
+ this.#viewModel._setAction(name, actionDescriptor.value);
93
+ }
94
+ const binderDescriptor = Object.getOwnPropertyDescriptor(
95
+ returnValue,
96
+ AttributeDefHelper.#binderSlot,
97
+ );
98
+ if (binderDescriptor) {
99
+ this.#viewModel._setBinder(name, binderDescriptor.value);
100
+ }
101
+ }
102
+ }
103
+
104
+ attribute<T extends AttributeDefinition & { as?: undefined }>(
105
+ action: AttributeAction<ModelT, T>,
106
+ binder?:
107
+ | AttributeBinder<ModelT, T>
108
+ | ViewModel<any, ReturnType<T>["namedDefinition"]>,
109
+ ): T;
110
+ attribute<T extends AttributeDefinition>(
111
+ action: AttributeAction<ModelT, T>,
112
+ binder: AttributeBinder<ModelT, T>,
113
+ ): T;
114
+ attribute(
115
+ action: any,
116
+ binder?: AttributeBinder<any, any> | ViewModel<any, any>,
117
+ ) {
118
+ if (binder instanceof ViewModel) {
119
+ const vm = binder;
120
+ binder = (model, positionals, named) => {
121
+ return vm.parse(named);
122
+ };
123
+ }
124
+ binder ??= () => {};
125
+ const returnValue = {};
126
+ Object.defineProperty(returnValue, AttributeDefHelper.#actionSlot, {
127
+ value: action,
128
+ enumerable: true,
129
+ });
130
+ Object.defineProperty(returnValue, AttributeDefHelper.#binderSlot, {
131
+ value: binder,
132
+ enumerable: true,
133
+ });
134
+ return returnValue;
135
+ }
136
+ }
137
+
138
+ export function defineViewModel<
139
+ T,
140
+ const BlockDef extends Record<string, AttributeDefinition>,
141
+ InitMeta = void,
142
+ >(
143
+ Ctor: new () => T,
144
+ modelDefFn: (helper: AttributeDefHelper<T>) => BlockDef,
145
+ initMeta?: InitMeta,
146
+ ): ViewModel<T, BlockDef & { [Meta]: InitMeta }> {
147
+ const vm = new ViewModel<T, BlockDef & { [Meta]: InitMeta }>(Ctor);
148
+ const helper = new AttributeDefHelper(vm);
149
+ const defResult = modelDefFn(helper);
150
+ helper._assignActions(defResult);
151
+ return vm;
152
+ }
153
+
154
+ interface AttributeDefinition {
155
+ (...args: any[]): AttributePositionalReturnBase;
156
+ as?(): any;
157
+ // required?(): boolean;
158
+ }
159
+
160
+ interface AttributePositionalReturnBase {
161
+ rewriteMeta?: any;
162
+ namedDefinition: AttributeBlockDefinition;
163
+ }
164
+
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
+ export type AttributeAction<Model, T extends AttributeDefinition> = (
194
+ model: Model,
195
+ positional: () => Parameters<T>,
196
+ named: View<
197
+ ReturnType<T>["namedDefinition"] extends AttributeBlockDefinition
198
+ ? ReturnType<T>["namedDefinition"]
199
+ : { [Meta]: void }
200
+ >,
201
+ ) => void;
202
+
203
+ export type AttributeBinder<Model, T extends AttributeDefinition> = (
204
+ model: Model,
205
+ positional: () => Parameters<T>,
206
+ named: View<
207
+ ReturnType<T>["namedDefinition"] extends AttributeBlockDefinition
208
+ ? ReturnType<T>["namedDefinition"]
209
+ : { [Meta]: void }
210
+ >,
211
+ ) => T["as"] extends () => infer U ? U : void;