aboutsummaryrefslogtreecommitdiff
path: root/src/fields/ScriptField.ts
blob: dbca74720f0c265e373008e8c41aebc88e6e17ee (plain)
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
import { ObjectField } from "../new_fields/ObjectField";
import { CompiledScript, CompileScript } from "../client/util/Scripting";
import { Copy, ToScriptString, Parent, SelfProxy } from "../new_fields/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";

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()))
});

function deserializeScript(script: ScriptField) {
    const comp = CompileScript(script.scriptString, script.options);
    if (!comp.compiled) {
        throw new Error("Couldn't compile loaded script");
    }
    (script as any)._script = comp;
}

@Deserializable("script", deserializeScript)
export class ScriptField extends ObjectField {
    protected readonly _script: CompiledScript;

    constructor(script: CompiledScript) {
        super();

        this._script = script;
    }

    @serializable(custom(object(optionsSchema).serializer, () => SKIP))
    get options() {
        return this._script && this._script.options;
    }

    @serializable(custom(primitive().serializer, () => SKIP))
    get scriptString(): string {
        return this._script && this._script.originalScript;
    }

    //     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";
    }
}

@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(doc: Doc) {
        const val = this._script.run({ this: doc });
        if (val.success) {
            return val.result;
        }
        return undefined;
    }
}