1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
import { ObjectField } from "./ObjectField";
import { CompiledScript, CompileScript, scriptingGlobal } from "../client/util/Scripting";
import { Copy, ToScriptString, Parent, SelfProxy } from "./FieldSymbols";
import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr";
import { Deserializable } from "../client/util/SerializationHelper";
import { Doc } from "../new_fields/Doc";
import { Plugins } from "./util";
import { computedFn } from "mobx-utils";
function optional(propSchema: PropSchema) {
return custom(value => {
if (value !== undefined) {
return propSchema.serializer(value);
}
return SKIP;
}, (jsonValue: any, context: any, oldValue: any, callback: (err: any, result: any) => void) => {
if (jsonValue !== undefined) {
return propSchema.deserializer(jsonValue, callback, context, oldValue);
}
return SKIP;
});
}
const optionsSchema = createSimpleSchema({
requiredType: true,
addReturn: true,
typecheck: true,
readonly: true,
params: optional(map(primitive()))
});
const scriptSchema = createSimpleSchema({
options: object(optionsSchema),
originalScript: true
});
function deserializeScript(script: ScriptField) {
const comp = CompileScript(script.script.originalScript, script.script.options);
if (!comp.compiled) {
throw new Error("Couldn't compile loaded script");
}
(script as any).script = comp;
}
@scriptingGlobal
@Deserializable("script", deserializeScript)
export class ScriptField extends ObjectField {
@serializable(object(scriptSchema))
readonly script: CompiledScript;
constructor(script: CompiledScript) {
super();
this.script = script;
}
// init(callback: (res: Field) => any) {
// const options = this.options!;
// const keys = Object.keys(options.options.capturedIds);
// Server.GetFields(keys).then(fields => {
// let captured: { [name: string]: Field } = {};
// keys.forEach(key => captured[options.options.capturedIds[key]] = fields[key]);
// const opts: ScriptOptions = {
// addReturn: options.options.addReturn,
// params: options.options.params,
// requiredType: options.options.requiredType,
// capturedVariables: captured
// };
// const script = CompileScript(options.script, opts);
// if (!script.compiled) {
// throw new Error("Can't compile script");
// }
// this._script = script;
// callback(this);
// });
// }
[Copy](): ObjectField {
return new ScriptField(this.script);
}
[ToScriptString]() {
return "script field";
}
}
@scriptingGlobal
@Deserializable("computed", deserializeScript)
export class ComputedField extends ScriptField {
//TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc
value = computedFn((doc: Doc) => {
const val = this.script.run({ this: doc });
if (val.success) {
return val.result;
}
return undefined;
});
}
export namespace ComputedField {
let useComputed = true;
export function DisableComputedFields() {
useComputed = false;
}
export function EnableComputedFields() {
useComputed = true;
}
export function WithoutComputed<T>(fn: () => T) {
DisableComputedFields();
try {
return fn();
} finally {
EnableComputedFields();
}
}
Plugins.addGetterPlugin((doc, _, value) => {
if (useComputed && value instanceof ComputedField) {
return { value: value.value(doc), shouldReturn: true };
}
});
}
|